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,