Skip to content

RHIDP-16658: Consume backend-provided color for scalar aggregation KPI cards - #4582

Open
imykhno wants to merge 1 commit into
redhat-developer:mainfrom
imykhno:feat/update-scorecard-scalar-aggregation-response
Open

RHIDP-16658: Consume backend-provided color for scalar aggregation KPI cards#4582
imykhno wants to merge 1 commit into
redhat-developer:mainfrom
imykhno:feat/update-scorecard-scalar-aggregation-response

Conversation

@imykhno

@imykhno imykhno commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Hey, I just made a Pull Request!

In this PR, we added the result.aggregationChartDisplayColor attribute to the scalar aggregation response. The color will be calculated on the backend, which now returns only the correct value.

Dropping result.thresholds was not implemented in this PR, as we decided that the UI may still need threshold information for certain charts

This PR is for:

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or Updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)

…gregation response

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
@rhdh-gh-app

rhdh-gh-app Bot commented Sep 4, 2026

Copy link
Copy Markdown

Unexpected Changesets

The following changeset(s) reference packages that have not been changed in this PR:

  • /home/runner/work/rhdh-plugins/rhdh-plugins/workspaces/scorecard/.changeset/hungry-walls-burn.md: @red-hat-developer-hub/backstage-plugin-scorecard

Note that only changes that affect the published package require changesets, for example changes to tests and storybook stories do not require changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-scorecard-backend workspaces/scorecard/plugins/scorecard-backend patch v4.2.0
@red-hat-developer-hub/backstage-plugin-scorecard-common workspaces/scorecard/plugins/scorecard-common patch v4.2.0

@rhdh-qodo-merge

Copy link
Copy Markdown

PR Summary by Qodo

Return backend-calculated colors for scalar aggregation KPI cards

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds backend-calculated display colors to scalar aggregation responses.
• Introduces scalar-specific default thresholds and shared color resolution.
• Updates public types, fixtures, API reports, and regression tests.
Diagram

graph TD
  C["KPI Config"] --> S["Scalar Strategy"] --> H["Color Helper"] --> E["Threshold Evaluator"]
  S --> R["Scalar Response"] --> U["Frontend Contract"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Calculate colors in the frontend
  • ➕ Avoids expanding the scalar aggregation API response.
  • ➕ Allows each visualization to choose its own presentation color.
  • ➖ Duplicates backend threshold evaluation logic across clients.
  • ➖ Can produce inconsistent colors when client and backend rules diverge.
  • ➖ Requires every consumer to understand threshold ordering and defaults.

Recommendation: Keep color calculation in the backend as implemented. It establishes one source of truth for threshold interpretation and aligns scalar aggregation behavior with weighted-status aggregation; retaining thresholds also preserves chart and legend flexibility for clients.

Files changed (15) +192 / -52

Enhancement (4) +85 / -4
aggregationKPIs.tsDefine default scalar KPI thresholds +25/-0

Define default scalar KPI thresholds

• Introduces ordered success, warning, and error thresholds used when scalar aggregation configuration omits explicit rules.

workspaces/scorecard/plugins/scorecard-backend/src/constants/aggregationKPIs.ts

ScalarAggregationStrategy.tsResolve scalar aggregation display colors +20/-4

Resolve scalar aggregation display colors

• Calculates the scalar result color from configured or scalar-default thresholds and includes it in the response. Aggregation now fails with actionable configuration guidance when no colored rule matches, while time-series processing adopts the same scalar defaults.

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts

getAggregationChartDisplayColor.tsAdd shared threshold color helper +39/-0

Add shared threshold color helper

• Adds a reusable utility that evaluates a numeric value against ordered threshold rules and returns the matching rule's color.

workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/getAggregationChartDisplayColor.ts

aggregation.tsExtend the scalar aggregation result contract +1/-0

Extend the scalar aggregation result contract

• Makes the backend-calculated aggregation chart display color a required field of scalar aggregation results.

workspaces/scorecard/plugins/scorecard-common/src/types/aggregation.ts

Refactor (1) +2 / -18
WeightedStatusScoreAggregationStrategy.tsShare aggregation color resolution logic +2/-18

Share aggregation color resolution logic

• Replaces the weighted-status strategy's private color resolver with the reusable aggregation color helper.

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/WeightedStatusScoreAggregationStrategy.ts

Tests (7) +89 / -18
mockAggregatedMetricResult.tsAdd display color to scalar result fixture +1/-0

Add display color to scalar result fixture

• Extends the shared scalar aggregation fixture with the new backend-provided warning color.

workspaces/scorecard/plugins/scorecard-backend/fixtures/mockAggregatedMetricResult.ts

mockAggregationConfig.tsUse scalar-specific default thresholds in fixtures +8/-7

Use scalar-specific default thresholds in fixtures

• Updates mock scalar configurations to use the new KPI-specific default threshold rules instead of the generic number thresholds.

workspaces/scorecard/plugins/scorecard-backend/fixtures/mockAggregationConfig.ts

scalarAggregationStrategy.test.tsVerify scalar colors and threshold defaults +5/-5

Verify scalar colors and threshold defaults

• Updates strategy expectations for scalar-specific defaults and asserts backend-calculated colors for default and custom thresholds.

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/scalarAggregationStrategy.test.ts

mappers.test.tsInclude scalar display colors in mapper fixtures +2/-0

Include scalar display colors in mapper fixtures

• Extends scalar mapper inputs with the required aggregation chart display color.

workspaces/scorecard/plugins/scorecard-backend/src/service/mappers.test.ts

router.test.tsValidate scalar colors in API responses +6/-5

Validate scalar colors in API responses

• Updates router response assertions to expect backend-calculated scalar colors and scalar-specific default thresholds. It also converts the Backstage configuration import to a type-only import.

workspaces/scorecard/plugins/scorecard-backend/src/service/router.test.ts

getAggregationChartDisplayColor.test.tsTest shared aggregation color resolution +64/-0

Test shared aggregation color resolution

• Covers unmatched thresholds, rules without colors, and first-match ordering for the new color helper.

workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/getAggregationChartDisplayColor.test.ts

ScorecardHomepageCard.test.tsxUpdate homepage scalar card fixture +3/-1

Update homepage scalar card fixture

• Aligns the homepage scalar scorecard fixture with scalar-specific default thresholds and the required backend-provided display color.

workspaces/scorecard/plugins/scorecard/src/components/ScorecardHomepageSection/tests/ScorecardHomepageCard.test.tsx

Documentation (3) +16 / -12
hungry-walls-burn.mdDeclare patch releases for Scorecard packages +7/-0

Declare patch releases for Scorecard packages

• Adds a changeset documenting the scalar aggregation response update and patch releases for the backend, common, and frontend packages.

workspaces/scorecard/.changeset/hungry-walls-burn.md

report.api.mdDocument scalar display color response field +2/-1

Document scalar display color response field

• Updates the generated common-package API report to expose 'aggregationChartDisplayColor' on scalar aggregation results. It also reflects generated ordering changes for threshold color values.

workspaces/scorecard/plugins/scorecard-common/report.api.md

report.api.mdRefresh the frontend API report +7/-11

Refresh the frontend API report

• Refreshes generated frontend API declaration ordering without changing the exposed Scorecard behavior.

workspaces/scorecard/plugins/scorecard/report.api.md

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 4, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 12:55 PM UTC · Completed 1:03 PM UTC

Commit: ef8296c · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Cost: $1.33

@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@rhdh-qodo-merge

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Optional colors now fail requests 🐞 Bug ≡ Correctness
Description
A valid scalar threshold rule using a standard key without its optional color now makes
aggregate throw instead of applying the documented standard color. Existing configurations that
previously rendered with default success, warning, or error colors will therefore fail their
aggregation request.
Code

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[R77-80]

+    if (!aggregationChartDisplayColor) {
+      throw new Error(
+        `The color for value '${value}' metric '${metric.id}' is not configured. Check the 'scorecard.aggregationKPIs.${aggregationConfig.id}.options.thresholds' configuration.`,
+      );
Relevance

●●● Strong

Optional colors require standard defaults; throwing on valid configurations is a concrete
backward-compatibility bug.

PR-#4395

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ThresholdRule.color is optional and standard keys are explicitly documented as having defaults.
The new helper returns the raw optional color, while the existing classifier applies
withStandardThresholdDefaults; the strategy now throws when the raw lookup returns undefined.

workspaces/scorecard/plugins/scorecard-common/src/types/threshold.ts[34-43]
workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/getAggregationChartDisplayColor.ts[30-38]
workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/classifyNumberAgainstThresholds.ts[29-41]
workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/withStandardThresholdDefaults.ts[40-52]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Scalar aggregation directly reads the matched rule's optional `color` and throws when it is absent, even though standard threshold keys have defined default colors.

## Issue Context
Use the existing threshold classification/defaulting path so `success`, `warning`, and `error` rules without explicit colors receive their standard colors. Keep appropriate handling for genuinely unresolvable custom rules.

## Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[72-81]
- workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/getAggregationChartDisplayColor.ts[26-38]
- workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/classifyNumberAgainstThresholds.ts[24-41]
- workspaces/scorecard/plugins/scorecard-backend/src/utils/aggregation/withStandardThresholdDefaults.ts[40-52]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Failed samples report success color 🐞 Bug ≡ Correctness
Description
The strategy evaluates the placeholder value 0 even when total is zero, causing the default
<10 rule to return success.main for empty or all-failed aggregations. Consumers of the new
server-provided field can consequently display a successful KPI despite there being no successful
sample.
Code

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[R72-75]

+    const aggregationChartDisplayColor = getAggregationChartDisplayColor(
+      value,
+      headlineThresholds,
+    );
Relevance

●●● Strong

Recent precedent accepted skipping threshold coloring when scalar aggregations have no successful
samples.

PR-#4395

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The database aggregation excludes missing/error values and defaults an empty aggregate to `value: 0,
total: 0`; the mapper preserves those defaults. The new unconditional color lookup then matches the
default <10 success rule, while the scalar card's existing guard confirms zero-total results
require a neutral color.

workspaces/scorecard/plugins/scorecard-backend/src/database/DatabaseMetricValues.ts[215-235]
workspaces/scorecard/plugins/scorecard-backend/src/service/mappers.ts[54-66]
workspaces/scorecard/plugins/scorecard-backend/src/constants/aggregationKPIs.ts[56-65]
workspaces/scorecard/plugins/scorecard/src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.tsx[66-78]
PR-#4395

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Scalar aggregation derives a threshold color from `value` even when no successful rows contributed to that value.

## Issue Context
The scalar mapper uses zero as the placeholder when no value exists, while `total` identifies whether successful samples contributed. Skip threshold coloring when `total === 0`, represent the response color as nullable or another explicit neutral state, and retain the card's grey fallback.

## Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[59-80]
- workspaces/scorecard/plugins/scorecard-common/src/types/aggregation.ts[109-112]
- workspaces/scorecard/plugins/scorecard/src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.tsx[66-78]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Default direction documented backwards 🐞 Bug ⚙ Maintainability
Description
The new default's comment says higher values are better, but its rules classify low values as
success and high values as error. This misleading contract can cause future changes to invert scalar
KPI threshold behavior.
Code

workspaces/scorecard/plugins/scorecard-backend/src/constants/aggregationKPIs.ts[R51-52]

+ * Default applied by `ScalarAggregationStrategy` when `options.thresholds` is omitted
+ * from app-config. Higher value = better. Evaluated in order; first match wins.
Relevance

●●● Strong

The comment directly contradicts its adjacent threshold rules; correcting this documentation is a
trivial deterministic fix.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The comment explicitly states higher values are better, while the immediately following rules assign
success to <10 and error to >50.

workspaces/scorecard/plugins/scorecard-backend/src/constants/aggregationKPIs.ts[50-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The scalar default threshold comment describes the opposite direction from the implemented rules.

## Issue Context
The rules implement lower-is-better behavior: values below 10 succeed and values above 50 fail.

## Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend/src/constants/aggregationKPIs.ts[50-70]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

4. Scalar card ignores response color 🐞 Bug ≡ Correctness
Description
The new aggregationChartDisplayColor response field has no effect on scalar KPI cards because
ScalarStatCard still derives its color locally from result.thresholds. This defeats the
server-authoritative color behavior that the response addition is intended to provide.
Code

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[89]

+      aggregationChartDisplayColor,
Relevance

● Weak

Recent precedent explicitly rejected requiring scalar cards to consume the backend-provided color.

PR-#4395

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The backend adds the computed field to the scalar result, while the scalar card only calls
getMatchingThresholdKey and resolves the threshold rule color locally. The weighted card
demonstrates the intended consumption pattern by passing result.aggregationChartDisplayColor to
resolveStatusColor.

workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[72-90]
workspaces/scorecard/plugins/scorecard/src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.tsx[66-78]
workspaces/scorecard/plugins/scorecard/src/components/AggregatedMetricCards/WeightedStatusScoreCard/WeightedStatusScoreCardComponent.tsx[74-79]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Scalar aggregation responses now include `aggregationChartDisplayColor`, but `ScalarStatCard` ignores it and continues deriving the display color from thresholds.

## Issue Context
Weighted status cards already consume the backend-provided field. Preserve the scalar card's neutral fallback for `total === 0`, but use the backend-provided color for successful samples.

## Fix Focus Areas
- workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/ScalarAggregationStrategy.ts[83-90]
- workspaces/scorecard/plugins/scorecard/src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.tsx[66-78]
- workspaces/scorecard/plugins/scorecard/src/components/AggregatedMetricCards/ScalarStatCard/ScalarStatCard.tsx[107-113]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 11 rules
✅ Cross-repo context — repo relationships
  Explored: repo: redhat-developer/rhdh (sha: 506c86ab)

Grey Divider

Tip of the day
💡 Did you know, you can route each action level your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@rhdh-qodo-merge rhdh-qodo-merge Bot added documentation Improvements or additions to documentation enhancement New feature or request Tests labels Sep 4, 2026
@fullsend-ai-review

Copy link
Copy Markdown

Review — comment

PR: #4582RHIDP-16658: Consume backend-provided color for scalar aggregation KPI cards

Summary

This PR adds backend-computed aggregationChartDisplayColor to scalar aggregation results, matching the existing pattern in WeightedStatusScoreAggregationStrategy. The implementation is well-structured:

  • Extracts getAggregationChartDisplayColor into a shared utility, eliminating duplication between the weighted-status-score and scalar strategies.
  • Introduces DEFAULT_SCALAR_AGGREGATION_KPI_RESULT_THRESHOLDS with explicit colors (unlike the old DEFAULT_NUMBER_THRESHOLDS which was colorless), enabling meaningful color computation for the default path.
  • Adds a required aggregationChartDisplayColor: string field to the ScalarAggregationResult type in scorecard-common.
  • Test coverage is solid across the new utility, strategy, mappers, and router tests.

The refactor is faithful — getAggregationChartDisplayColor is identical in logic to the private method it replaces in WeightedStatusScoreAggregationStrategy, and the error-throw guard on missing color is consistent with the weighted strategy.

Findings

1. Cross-package deep import from frontend test into backend src/ (medium — style/conventions)

ScorecardHomepageCard.test.tsx introduces:

import { DEFAULT_SCALAR_AGGREGATION_KPI_RESULT_THRESHOLDS }
  from '@red-hat-developer-hub/backstage-plugin-scorecard-backend/src/constants/aggregationKPIs';

This is the first cross-boundary deep import from the scorecard frontend plugin into the backend's internal src/ tree. It bypasses the backend's public API surface and creates a fragile test-time dependency — any restructuring of the backend's internal modules would break this test.

Remediation: Either re-export this constant from scorecard-common (since both backend and frontend need it), or define the threshold fixture inline in the test. Since the constant represents default backend behavior, re-exporting from scorecard-common alongside DEFAULT_NUMBER_THRESHOLDS is the cleaner approach.

2. Misleading JSDoc comment on new thresholds constant (low — documentation)

The comment on DEFAULT_SCALAR_AGGREGATION_KPI_RESULT_THRESHOLDS in aggregationKPIs.ts states:

Higher value = better. Evaluated in order; first match wins.

But the rules define <10 → success and >50 → error, which means lower values are better. The neighboring DEFAULT_WEIGHTED_STATUS_SCORE_KPI_RESULT_THRESHOLDS correctly says "Higher headline percentage = better" where >=80 is success. The new comment appears to be copy-pasted without updating the direction.

Remediation: Change "Higher value = better" to "Lower value = better" (or "High count indicates problems" to match the DEFAULT_NUMBER_THRESHOLDS JSDoc it replaces).

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request requires-manual-review Review requires human judgment Tests workspace/scorecard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant