Skip to content

RHIDP-14173: Implement scalar aggregation KPI types#3571

Draft
imykhno wants to merge 5 commits into
redhat-developer:mainfrom
imykhno:feat/scorecard-scalar-aggregation-types
Draft

RHIDP-14173: Implement scalar aggregation KPI types#3571
imykhno wants to merge 5 commits into
redhat-developer:mainfrom
imykhno:feat/scorecard-scalar-aggregation-types

Conversation

@imykhno

@imykhno imykhno commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Hey, I just made a Pull Request!

Implemented logic to support the following aggregate metric types: average, sum, max, min, and count for Scorecard aggregated KPI cards.

This PR 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)

How to test

Preparing

  • If using app-config.local.yaml add new aggregation types under scorecard.aggregationKPIs (example added to the app-config.yaml):
scorecard:
  aggregationKPIs:
    totalOpenBugs:
      title: Total Open Bugs
      description: Sum of open issues across owned entities
      type: sum
      metricId: jira.open_issues
      options:
        thresholds:
          rules:
            - key: success
              expression: '>=80'
              color: '#6bb300' # green
            - key: warning
              expression: '<80'
              color: 'rgb(224, 189, 108)' # light orange
    avgOpenPrs:
      title: Average Open PRs
      description: Mean open PR count per entity
      type: average
      metricId: github.open_prs
    entitiesWithOpenIssues:
      title: Entities with Open Issues
      description: Count of entities with a stored open-issues value
      type: count
      metricId: jira.open_issues
    maxOpenPrs:
      title: Maximum Open PRs
      description: Maximum open PR count per entity
      type: max
      metricId: github.open_prs
    minOpenPrs:
      title: Minimum Open PRs
      description: Minimum open PR count per entity
      type: min
      metricId: github.open_prs
  • Set environment variables for convenience
export BACKSTAGE_URL="http://localhost:7007"
export TOKEN="your-auth-token-here"

Testing

Get aggregation result when sum aggregation type was used (totalOpenBugs):

curl -X GET "${BACKSTAGE_URL}/api/scorecard/aggregations/totalOpenBugs" \
  -H "Authorization: Bearer ${TOKEN}" | jq

Get aggregation result when average aggregation type was used (avgOpenPrs):

curl -X GET "${BACKSTAGE_URL}/api/scorecard/aggregations/avgOpenPrs" \
  -H "Authorization: Bearer ${TOKEN}" | jq

Get aggregation result when count aggregation type was used (entitiesWithOpenIssues):

curl -X GET "${BACKSTAGE_URL}/api/scorecard/aggregations/entitiesWithOpenIssues" \
  -H "Authorization: Bearer ${TOKEN}" | jq

Get aggregation result when max aggregation type was used (maxOpenPrs):

curl -X GET "${BACKSTAGE_URL}/api/scorecard/aggregations/maxOpenPrs" \
  -H "Authorization: Bearer ${TOKEN}" | jq

Get aggregation result when min aggregation type was used (minOpenPrs):

curl -X GET "${BACKSTAGE_URL}/api/scorecard/aggregations/minOpenPrs" \
  -H "Authorization: Bearer ${TOKEN}" | jq

Get aggregation results when some entities lack values due to an error

The environment must contain a mix of entities: some with valid values for aggregation, and others without values to simulate an error state.

Use one of the proposed requests below to confirm that:

  • Aggregation only occurs for entities with valid values.
  • The total count of entities with errors is correctly populated in the calculationErrorCount attribute.

Get aggregation results when all entities lack values due to an error

The environment must contain a all of entities without values to simulate an error state.

Use one of the proposed requests below to confirm that:

  • The total count of entities with errors is correctly populated in the calculationErrorCount attribute.

When the threshold is incorrectly configured for new aggregation types

Configure a threshold with incorrect ranges for a new aggregation type. The application is expected to throw an error upon startup.

For example:

    wrongThresholds:
      title: Wrong Thresholds
      description: Wrong thresholds
      type: sum
      metricId: jira.open_issues
      options:
        thresholds:
          rules:
            - key: success
              expression: '>=80'
              color: '#6bb300' # green
            - key: warning
              expression: '<20'
              color: 'rgb(224, 189, 108)' # light orange

When the threshold is correctly configured for new aggregation types

A correctly configured threshold should be present in the response under result.thresholds. If no threshold is configured under app-config.yaml, the default threshold should be used.

When a new aggregation type is configured for a boolean provider

When a metricId for a boolean metric provider is supplied, an error should be thrown when the application starts up.

For example:

    licenseFileExistsKpi:
      title: License File Exists KPI
      type: sum
      description: This KPI is provide information about whether the license file exists in the repository.
      metricId: filecheck.license

@github-actions

Copy link
Copy Markdown
Contributor

This pull request adds a new top-level directory under workspaces/. Please follow Submitting a Pull Request for a New Workspace in CONTRIBUTING.md.

@rhdh-gh-app

rhdh-gh-app Bot commented Jun 24, 2026

Copy link
Copy Markdown

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Changed Packages

Package Name Package Path Changeset Bump Current Version
app-legacy workspaces/scorecard/packages/app-legacy none v0.0.0
@red-hat-developer-hub/backstage-plugin-scorecard-backend workspaces/scorecard/plugins/scorecard-backend major v2.7.9
@red-hat-developer-hub/backstage-plugin-scorecard-common workspaces/scorecard/plugins/scorecard-common major v2.7.9
@red-hat-developer-hub/backstage-plugin-scorecard workspaces/scorecard/plugins/scorecard major v2.7.9

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 24, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:02 PM UTC · Completed 5:19 PM UTC
Commit: a50b883 · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review

Findings

Low

  • [Documentation / code mismatch] workspaces/scorecard/plugins/scorecard/README.md — The README (as modified by this PR) states the built-in card renders statusGrouped, weightedStatusScore, and scalar types. However, the AggregatedMetricCard component only handles statusGrouped and weightedStatusScore; all scalar types fall through to UnsupportedAggregationType. The documentation overstates current frontend capability.
    Remediation: Adjust the README sentence to clarify that scalar types are supported by the backend but not yet rendered by the built-in frontend card, or add a note that scalar card rendering is pending.

  • [Test integrity / assertion removal] workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/statusGroupedAggregationStrategy.test.ts — The test "delegates empty entityRefs to loader" had its assertion on result.result.values removed. The remaining assertions only check total, entitiesConsidered, and calculationErrorCount. While the first test in the file covers values mapping in detail, consider restoring an assertion on the values array shape for this empty-entityRefs case.

  • [Truthiness check on numeric value] workspaces/scorecard/plugins/scorecard-backend/src/database/DatabaseMetricValues.ts — In readScalarAggregationByLatestIdsSubquery, const value = aggregateRow?.value ? Number(aggregateRow.value) : 0 uses a truthiness check. If the SQL aggregate returns 0, the truthiness check treats it as falsy and falls through to 0 — which is correct by coincidence. A != null check would express the intent more clearly and be resilient to future changes.

Previous run

Review

Findings

Medium

  • [Documentation/code mismatch] workspaces/scorecard/plugins/scorecard/README.md:9 — The README states "The built-in card renders statusGrouped (multi-slice pie), weightedStatusScore (weighted health donut), and scalar types (sum, average, max, min, count)." However, the AggregatedMetricCard component only handles statusGrouped and weightedStatusScore via explicit type guards; all scalar types fall through to UnsupportedAggregationType (an error panel). The README overstates the frontend's current rendering capability.
    Remediation: Update the README to clarify that scalar types are supported by the backend API but do not yet have a dedicated frontend card component, or remove the scalar types from the frontend README's feature bullet.

Low

  • [Test coverage reduction] workspaces/scorecard/plugins/scorecard-backend/src/service/aggregations/strategies/statusGroupedAggregationStrategy.test.ts:123 — The "delegates empty entityRefs to loader" test previously asserted that result.result.values contained per-threshold-rule entries with count 0 (error, warning, success). This assertion was removed without replacement, reducing test coverage for the StatusGroupedAggregationStrategy's behavior with empty inputs.

  • [Falsy check on numeric value] workspaces/scorecard/plugins/scorecard-backend/src/database/DatabaseMetricValues.ts — In readScalarAggregationByLatestIdsSubquery, the expression aggregateRow?.value ? Number(aggregateRow.value) : 0 uses a truthiness check on a numeric value. When the SQL aggregate returns 0 (e.g., SUM of all-zero values), the check is falsy and falls to the default 0 — numerically correct by coincidence but fragile. A stricter null/undefined check like aggregateRow?.value != null ? Number(aggregateRow.value) : 0 would be more robust.

Previous run (2)

Review

Findings

Medium

  • [consumer-completeness] plugins/scorecard/src/components/AggregatedMetricCards/AggregatedMetricCard.tsx — The new scalar aggregation types (sum, average, max, min, count) are registered in the backend strategy registry and returned via the API, but the frontend AggregatedMetricCard component only handles statusGrouped and weightedStatusScore. Scalar types fall through to UnsupportedAggregationType, rendering an error panel. The app-config.yaml in this PR adds scalar KPI entries (totalOpenBugs type:sum, avgOpenPrs type:average, etc.) that would trigger this error on the homepage.
    Remediation: Either add a scalar card renderer in the frontend, remove the scalar KPI entries from app-config.yaml, or document that scalar types are backend-only and homepage rendering is planned for a follow-up.

Low

  • [edge-case] plugins/scorecard-backend/src/database/DatabaseMetricValues.ts — In readScalarAggregationByLatestIdsSubquery, using truthiness check (value ? Number(value) : 0) means a legitimate computed value of 0 will also be treated as 0, which is coincidentally correct but fragile. If a future change uses a different sentinel, this could break.

  • [test-adequacy] plugins/scorecard-backend/src/service/aggregations/strategies/statusGroupedAggregationStrategy.test.ts — The test for empty entityRefs removed the assertion that verified the values array contains zeroed counts per threshold rule. Coverage for this specific invariant is reduced. Consider restoring the removed assertion.


Labels: PR implements new scalar aggregation KPI types as a feature enhancement to the scorecard workspace.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment enhancement New feature or request labels Jun 24, 2026
@imykhno imykhno force-pushed the feat/scorecard-scalar-aggregation-types branch from 22368af to 5a7cfb0 Compare June 24, 2026 17:28
@fullsend-ai-review

fullsend-ai-review Bot commented Jun 24, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 5:31 PM UTC · Ended 5:44 PM UTC
Commit: 6c9cfa5 · View workflow run →

@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.93617% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.26%. Comparing base (381f36a) to head (53fdb59).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3571      +/-   ##
==========================================
+ Coverage   54.19%   54.26%   +0.06%     
==========================================
  Files        2303     2309       +6     
  Lines       88305    88434     +129     
  Branches    24590    24630      +40     
==========================================
+ Hits        47858    47989     +131     
+ Misses      40238    40236       -2     
  Partials      209      209              
Flag Coverage Δ *Carryforward flag
adoption-insights 83.70% <ø> (ø) Carriedforward from 132f773
ai-integrations 67.95% <ø> (ø) Carriedforward from 132f773
app-defaults 69.79% <ø> (ø) Carriedforward from 132f773
augment 46.39% <ø> (ø) Carriedforward from 132f773
boost 74.05% <ø> (ø) Carriedforward from 132f773
bulk-import 72.46% <ø> (ø) Carriedforward from 132f773
cost-management 14.10% <ø> (ø) Carriedforward from 132f773
dcm 61.79% <ø> (ø) Carriedforward from 132f773
extensions 61.53% <ø> (ø) Carriedforward from 132f773
global-floating-action-button 71.18% <ø> (ø) Carriedforward from 132f773
global-header 59.71% <ø> (ø) Carriedforward from 132f773
homepage 49.84% <ø> (ø) Carriedforward from 132f773
install-dynamic-plugins 56.77% <ø> (ø) Carriedforward from 132f773
konflux 91.49% <ø> (ø) Carriedforward from 132f773
lightspeed 68.55% <ø> (ø) Carriedforward from 132f773
mcp-integrations 85.46% <ø> (ø) Carriedforward from 132f773
orchestrator 38.30% <ø> (ø) Carriedforward from 132f773
quickstart 63.76% <ø> (ø) Carriedforward from 132f773
sandbox 79.56% <ø> (ø) Carriedforward from 132f773
scorecard 84.63% <98.93%> (+0.67%) ⬆️
theme 61.26% <ø> (ø) Carriedforward from 132f773
translations 7.25% <ø> (ø) Carriedforward from 132f773
x2a 78.68% <ø> (ø) Carriedforward from 132f773

*This pull request uses carry forward flags. Click here to find out more.


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 381f36a...53fdb59. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@fullsend-ai-review fullsend-ai-review Bot added requires-manual-review Review requires human judgment and removed requires-manual-review Review requires human judgment labels Jun 24, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 5:31 PM UTC · Completed 5:44 PM UTC
Commit: 6c9cfa5 · View workflow run →

imykhno added 5 commits June 25, 2026 14:48
…dStatusScore`

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
… safety and improved structure

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
…dKpi` across tests and configurations

Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
Signed-off-by: Ihor Mykhno <imykhno@redhat.com>
@imykhno imykhno force-pushed the feat/scorecard-scalar-aggregation-types branch from 5a7cfb0 to 53fdb59 Compare June 25, 2026 13:01
@sonarqubecloud

Copy link
Copy Markdown

@fullsend-ai-review

fullsend-ai-review Bot commented Jun 25, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 1:04 PM UTC · Completed 1:17 PM UTC
Commit: 381f36a · View workflow run →

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge and removed requires-manual-review Review requires human judgment labels Jun 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress enhancement New feature or request ready-for-merge All reviewers approved — ready to merge workspace/scorecard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant