From 3f3c29607f56252d8f0aa6e6d0389e7ed36117f1 Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Tue, 28 Apr 2026 13:57:30 -0600 Subject: [PATCH] [UI] Add total credential units + GCP KMS in data protection calls (#14312) (#14367) * Add tests! * Add gcp kms value * Update to use enum * Update metric helper test Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com> --- ui/app/components/billing/metric-card.hbs | 5 ++++- ui/app/components/billing/metric-card.ts | 13 ++++++++++++ ui/app/components/billing/page/overview.hbs | 19 +++++++++++++---- ui/app/components/billing/page/overview.ts | 4 ++++ ui/app/components/billing/summary-card.ts | 6 +++--- ui/app/utils/metrics-helpers.ts | 22 ++++++++++++++++++++ ui/tests/acceptance/billing/overview-test.js | 9 ++++++++ ui/tests/helpers/billing/stubs.ts | 11 ++++++++++ ui/tests/unit/utils/metric-helpers-test.js | 5 +++++ 9 files changed, 86 insertions(+), 8 deletions(-) diff --git a/ui/app/components/billing/metric-card.hbs b/ui/app/components/billing/metric-card.hbs index 6f0b96067d..8642ada8af 100644 --- a/ui/app/components/billing/metric-card.hbs +++ b/ui/app/components/billing/metric-card.hbs @@ -18,8 +18,11 @@ {{or this.total "0"}} {{#each-in @metrics as |metricKey metricValue|}} + {{#if (eq metricKey this.normalizedBillableMetrics.ID_TOKEN_UNITS_OIDC)}} + + {{/if}} {{#let (this.metricDetails metricKey) as |display|}} - + {{#if display.tooltipText}} {{display.label}} diff --git a/ui/app/components/billing/metric-card.ts b/ui/app/components/billing/metric-card.ts index 6ca2b9e02d..be969b9cb0 100644 --- a/ui/app/components/billing/metric-card.ts +++ b/ui/app/components/billing/metric-card.ts @@ -14,6 +14,8 @@ interface Args { } export default class MetricCard extends Component { + normalizedBillableMetrics = NormalizedBillingMetrics; + get total() { const sums = Object.values(this.args.metrics).filter((metric) => metric !== undefined); return calculateSum(sums); @@ -55,6 +57,14 @@ export default class MetricCard extends Component { tooltipText: 'Total number of SSH one-time passwords issued, normalized by their duration. Each OTP is 0.0014 units.', }, + [NormalizedBillingMetrics.ID_TOKEN_UNITS_OIDC]: { + label: 'OIDC token units', + tooltipText: 'Total number of ID tokens issued, normalized by their duration.', + }, + [NormalizedBillingMetrics.ID_TOKEN_UNITS_SPIFFE]: { + label: 'SPIFFE JWT units', + tooltipText: 'Total number of SPIFFE JWT tokens issued, normalized by their duration.', + }, [NormalizedBillingMetrics.SSH_UNITS_CERTIFICATE_UNITS]: { label: 'SSH certificate units', tooltipText: 'Total number of SSH certificates issued, normalized by their duration.', @@ -65,6 +75,9 @@ export default class MetricCard extends Component { [NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TRANSFORM]: { label: 'Transform', }, + [NormalizedBillingMetrics.DATA_PROTECTION_CALLS_GCPKMS]: { + label: 'GCP KMS', + }, [NormalizedBillingMetrics.MANAGED_KEYS_TOTP]: { label: 'TOTP', }, diff --git a/ui/app/components/billing/page/overview.hbs b/ui/app/components/billing/page/overview.hbs index c901a0e059..529dbcf58e 100644 --- a/ui/app/components/billing/page/overview.hbs +++ b/ui/app/components/billing/page/overview.hbs @@ -12,9 +12,6 @@ Data reflects usage across this Vault cluster. Billing metrics determine license utilization. - <:actions> - - - Details by metric + + Details by metric + + {{#each-in this.detailsByMetric as |cardTitle cardKey|}} diff --git a/ui/app/components/billing/page/overview.ts b/ui/app/components/billing/page/overview.ts index fe1721ba8b..c8246be4ef 100644 --- a/ui/app/components/billing/page/overview.ts +++ b/ui/app/components/billing/page/overview.ts @@ -41,10 +41,13 @@ export default class BillingPageOverview extends Component { NormalizedBillingMetrics.PKI_UNITS_TOTAL, NormalizedBillingMetrics.SSH_UNITS_OTP_UNITS, NormalizedBillingMetrics.SSH_UNITS_CERTIFICATE_UNITS, + NormalizedBillingMetrics.ID_TOKEN_UNITS_OIDC, + NormalizedBillingMetrics.ID_TOKEN_UNITS_SPIFFE, ], 'Data protection calls': [ NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TRANSFORM, NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TRANSIT, + NormalizedBillingMetrics.DATA_PROTECTION_CALLS_GCPKMS, ], 'Managed keys': [NormalizedBillingMetrics.MANAGED_KEYS_TOTP, NormalizedBillingMetrics.MANAGED_KEYS_KMSE], }; @@ -135,6 +138,7 @@ export default class BillingPageOverview extends Component { @action onDateChange(dropdownOption: Month | null | undefined) { this.selectedDateOption = dropdownOption; + this.normalizedMetricData = normalizeMetricData(dropdownOption); } diff --git a/ui/app/components/billing/summary-card.ts b/ui/app/components/billing/summary-card.ts index bbefcc630c..a68f6aaa5c 100644 --- a/ui/app/components/billing/summary-card.ts +++ b/ui/app/components/billing/summary-card.ts @@ -24,7 +24,7 @@ interface Args { export default class SummaryCard extends Component { summaryMetricKeys = [ NormalizedBillingMetrics.STATIC_SECRETS_TOTAL, - NormalizedBillingMetrics.PKI_UNITS_TOTAL, + NormalizedBillingMetrics.CREDENTIAL_UNITS_TOTAL, NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TOTAL, NormalizedBillingMetrics.MANAGED_KEYS_TOTAL, NormalizedBillingMetrics.KMIP_USED_IN_MONTH, @@ -35,8 +35,8 @@ export default class SummaryCard extends Component { [NormalizedBillingMetrics.STATIC_SECRETS_TOTAL]: { label: 'Secrets', }, - [NormalizedBillingMetrics.PKI_UNITS_TOTAL]: { - label: 'PKI units', + [NormalizedBillingMetrics.CREDENTIAL_UNITS_TOTAL]: { + label: 'Credential units', }, [NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TOTAL]: { label: 'Data protection calls', diff --git a/ui/app/utils/metrics-helpers.ts b/ui/app/utils/metrics-helpers.ts index bbc1b3157d..1bbc152139 100644 --- a/ui/app/utils/metrics-helpers.ts +++ b/ui/app/utils/metrics-helpers.ts @@ -7,11 +7,16 @@ import type { Month, NormalizedMetricsData } from 'vault/vault/billing/overview' export enum NormalizedBillingMetrics { AUTO_ROTATED_ROLES_TOTAL = 'auto_rotated_roles_total', + CREDENTIAL_UNITS_TOTAL = 'credential_units_total', DATA_PROTECTION_CALLS_TOTAL = 'data_protection_calls_total', DATA_PROTECTION_CALLS_TRANSFORM = 'data_protection_calls_transform', DATA_PROTECTION_CALLS_TRANSIT = 'data_protection_calls_transit', + DATA_PROTECTION_CALLS_GCPKMS = 'data_protection_calls_gcpkms', DYNAMIC_ROLES_TOTAL = 'dynamic_roles_total', EXTERNAL_PLUGINS_TOTAL = 'external_plugins_total', + ID_TOKEN_UNITS_TOTAL = 'id_token_units_total', + ID_TOKEN_UNITS_OIDC = 'id_token_units_oidc', + ID_TOKEN_UNITS_SPIFFE = 'id_token_units_spiffe', KMIP_USED_IN_MONTH = 'kmip_used_in_month', MANAGED_KEYS = 'managed_keys', MANAGED_KEYS_KMSE = 'managed_keys_kmse', @@ -68,6 +73,23 @@ export function normalizeMetricData(metric: Month | null | undefined) { normalized[detailName] = detail.count; } } + + // Calculate credential_units_total as the sum of ssh_units, pki_units, and id_token_units + const sshUnitsTotal = + typeof normalized[NormalizedBillingMetrics.SSH_UNITS_TOTAL] === 'number' + ? normalized[NormalizedBillingMetrics.SSH_UNITS_TOTAL] + : 0; + const pkiUnitsTotal = + typeof normalized[NormalizedBillingMetrics.PKI_UNITS_TOTAL] === 'number' + ? normalized[NormalizedBillingMetrics.PKI_UNITS_TOTAL] + : 0; + const idTokenUnitsTotal = + typeof normalized[NormalizedBillingMetrics.ID_TOKEN_UNITS_TOTAL] === 'number' + ? normalized[NormalizedBillingMetrics.ID_TOKEN_UNITS_TOTAL] + : 0; + normalized[NormalizedBillingMetrics.CREDENTIAL_UNITS_TOTAL] = + sshUnitsTotal + pkiUnitsTotal + idTokenUnitsTotal; + // The API omits metrics that have zero usage rather than returning them with a count of 0. // To avoid blank values in the UI, we explicitly set any missing metric keys to 0. for (const metricsKey of Object.values(NormalizedBillingMetrics)) { diff --git a/ui/tests/acceptance/billing/overview-test.js b/ui/tests/acceptance/billing/overview-test.js index c4e8e974dd..fb92b24889 100644 --- a/ui/tests/acceptance/billing/overview-test.js +++ b/ui/tests/acceptance/billing/overview-test.js @@ -81,6 +81,12 @@ module('Acceptance | billing/overview', function (hooks) { assert .dom(SELECTORS.metricDetailValue(NormalizedBillingMetrics.SSH_UNITS_CERTIFICATE_UNITS)) .hasText('50.1234'); + assert.dom(SELECTORS.metricDetail(NormalizedBillingMetrics.ID_TOKEN_UNITS_OIDC)).exists(); + assert.dom(SELECTORS.metricDetailValue(NormalizedBillingMetrics.ID_TOKEN_UNITS_OIDC)).hasText('52.1234'); + assert.dom(SELECTORS.metricDetail(NormalizedBillingMetrics.ID_TOKEN_UNITS_SPIFFE)).exists(); + assert + .dom(SELECTORS.metricDetailValue(NormalizedBillingMetrics.ID_TOKEN_UNITS_SPIFFE)) + .hasText('51.1234'); assert.dom(GENERAL.cardContainer('Data protection calls')).exists(); assert.dom(SELECTORS.metricDetail(NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TRANSFORM)).exists(); @@ -91,6 +97,9 @@ module('Acceptance | billing/overview', function (hooks) { assert .dom(SELECTORS.metricDetailValue(NormalizedBillingMetrics.DATA_PROTECTION_CALLS_TRANSIT)) .hasText('200'); + assert + .dom(SELECTORS.metricDetailValue(NormalizedBillingMetrics.DATA_PROTECTION_CALLS_GCPKMS)) + .hasText('220'); assert.dom(GENERAL.cardContainer('Managed keys')).exists(); assert.dom(SELECTORS.metricDetail(NormalizedBillingMetrics.MANAGED_KEYS_TOTP)).exists(); diff --git a/ui/tests/helpers/billing/stubs.ts b/ui/tests/helpers/billing/stubs.ts index 8503151efc..cb87f0bebf 100644 --- a/ui/tests/helpers/billing/stubs.ts +++ b/ui/tests/helpers/billing/stubs.ts @@ -88,6 +88,7 @@ export const METRICS_DATA_RESPONSE = { metric_details: [ { type: 'transit', count: 200 }, { type: 'transform', count: 220 }, + { type: 'gcpkms', count: 220 }, // Added for GCP KMS data protection calls ], }, }, @@ -101,6 +102,16 @@ export const METRICS_DATA_RESPONSE = { ], }, }, + { + metric_name: 'id_token_units', // Added for ID token units (OIDC and SPIFFE) + metric_data: { + total: 103.2468, + metric_details: [ + { type: 'oidc', count: 52.1234 }, + { type: 'spiffe', count: 51.1234 }, + ], + }, + }, ], }, { diff --git a/ui/tests/unit/utils/metric-helpers-test.js b/ui/tests/unit/utils/metric-helpers-test.js index 064322bb16..fe265e6c06 100644 --- a/ui/tests/unit/utils/metric-helpers-test.js +++ b/ui/tests/unit/utils/metric-helpers-test.js @@ -74,11 +74,16 @@ module('Unit | Utility | metric utils', function () { }; const expected = { auto_rotated_roles_total: 0, + credential_units_total: 0, + data_protection_calls_gcpkms: 0, data_protection_calls_total: 0, data_protection_calls_transform: 0, data_protection_calls_transit: 0, dynamic_roles_total: 0, external_plugins_total: 0, + id_token_units_oidc: 0, + id_token_units_spiffe: 0, + id_token_units_total: 0, kmip_used_in_month: false, managed_keys: 0, managed_keys_kmse: 0,