From e8ef7285a4e481ef830220ea66ce1d62ec051d96 Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Tue, 11 Nov 2025 19:12:16 -0500 Subject: [PATCH] Backport UI: Render total monthly clients for HVD managed clusters and "new" for only self-managed into ce/main (#10703) * UI: Render total monthly clients for HVD managed clusters and "new" for only self-managed (#10472) * rename byMonthNewClients to byMonthClients * render total vs new clients depending on cluster type * fix mutation of original array order * add changelog * Apply suggestion from @hellobontempo * Apply suggestion from @hellobontempo * Apply suggestion from @hellobontempo * add test coverage * hide client list tab for HVD clusters * add test coverage * make copy changes * apply copy updates from feedback sync * skip hvd test on CE runs * restart tests --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Co-authored-by: claire bontempo --- changelog/_10472.txt | 3 + .../clients/charts/vertical-bar-stacked.ts | 2 +- ui/app/components/clients/counts-card.hbs | 6 +- ui/app/components/clients/date-range.hbs | 10 +-- ui/app/components/clients/date-range.ts | 8 ++- ui/app/components/clients/page-header.hbs | 2 +- ui/app/components/clients/page-header.js | 1 + ui/app/components/clients/page/counts.hbs | 8 ++- ui/app/components/clients/page/counts.ts | 2 + ui/app/components/clients/page/overview.hbs | 10 +-- ui/app/components/clients/page/overview.ts | 16 ++++- ui/app/components/clients/running-total.hbs | 18 +++--- ui/app/components/clients/running-total.ts | 28 ++++++--- ui/app/components/clients/table.hbs | 6 +- ui/app/components/clients/table.ts | 2 + ui/app/routes/vault/cluster/clients/counts.ts | 3 +- .../cluster/clients/counts/client-list.ts | 22 ++++++- ui/app/styles/core/charts.scss | 2 +- .../clients/counts/client-list-test.js | 18 ++++++ .../clients/counts/overview-test.js | 38 +++++------- .../components/clients/date-range-test.js | 29 ++++++++- .../components/clients/page-header-test.js | 21 +++++++ .../components/clients/page/overview-test.js | 33 +++++++++- .../components/clients/running-total-test.js | 62 +++++++++++++------ 24 files changed, 263 insertions(+), 87 deletions(-) create mode 100644 changelog/_10472.txt diff --git a/changelog/_10472.txt b/changelog/_10472.txt new file mode 100644 index 0000000000..489ea8a3ec --- /dev/null +++ b/changelog/_10472.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui/activity: Display total instead of new monthly clients for HCP managed clusters +``` \ No newline at end of file diff --git a/ui/app/components/clients/charts/vertical-bar-stacked.ts b/ui/app/components/clients/charts/vertical-bar-stacked.ts index 66747866cb..8c56776ebe 100644 --- a/ui/app/components/clients/charts/vertical-bar-stacked.ts +++ b/ui/app/components/clients/charts/vertical-bar-stacked.ts @@ -48,7 +48,7 @@ type ChartDatum = DatumBase & { * @example * diff --git a/ui/app/components/clients/counts-card.hbs b/ui/app/components/clients/counts-card.hbs index 62f75e90be..e1c64216bc 100644 --- a/ui/app/components/clients/counts-card.hbs +++ b/ui/app/components/clients/counts-card.hbs @@ -39,7 +39,11 @@ Data visualizations render in in a flex row with a 1/3-width left element and a
{{#each @legend as |l idx|}}
- + {{#if (includes l.key (array "clients" "new_clients"))}} + + {{else}} + + {{/if}} {{l.label}}
{{/each}} diff --git a/ui/app/components/clients/date-range.hbs b/ui/app/components/clients/date-range.hbs index 0d732aa76e..cd68b94cb2 100644 --- a/ui/app/components/clients/date-range.hbs +++ b/ui/app/components/clients/date-range.hbs @@ -7,14 +7,14 @@
{{#if this.version.isEnterprise}} - Change billing period + {{if this.flags.isHvdManaged "Change data period" "Change billing period"}} - - + + @@ -25,7 +25,7 @@ {{#each this.historicalBillingPeriods as |period idx|}} diff --git a/ui/app/components/clients/date-range.ts b/ui/app/components/clients/date-range.ts index 01c0710ea4..407927f6d2 100644 --- a/ui/app/components/clients/date-range.ts +++ b/ui/app/components/clients/date-range.ts @@ -11,6 +11,7 @@ import { buildISOTimestamp, parseAPITimestamp } from 'core/utils/date-formatters import timestamp from 'core/utils/timestamp'; import { format } from 'date-fns'; +import type FlagsService from 'vault/services/flags'; import type VersionService from 'vault/services/version'; import type { HTMLElementEvent } from 'forms'; @@ -46,6 +47,7 @@ interface Args { */ export default class ClientsDateRangeComponent extends Component { + @service declare readonly flags: FlagsService; @service declare readonly version: VersionService; @tracked modalStart = ''; // format yyyy-MM @@ -64,7 +66,8 @@ export default class ClientsDateRangeComponent extends Component { get historicalBillingPeriods() { // we want whole billing periods - const count = Math.floor(this.args.retentionMonths / 12); + const totalMonths = this.args.retentionMonths || 48; + const count = Math.floor(totalMonths / 12); const periods: string[] = []; for (let i = 1; i <= count; i++) { @@ -118,9 +121,10 @@ export default class ClientsDateRangeComponent extends Component { } @action - updateEnterpriseDateRange(start: string) { + updateEnterpriseDateRange(start: string, close: CallableFunction) { // We do not send an end_time so the backend handles computing the expected billing period this.args.onChange({ start_time: start, end_time: '' }); + close(); } // HELPERS diff --git a/ui/app/components/clients/page-header.hbs b/ui/app/components/clients/page-header.hbs index 39cf720d8f..31f8213586 100644 --- a/ui/app/components/clients/page-header.hbs +++ b/ui/app/components/clients/page-header.hbs @@ -26,7 +26,7 @@ {{#if this.version.isEnterprise}}

- For billing period: + {{if this.flags.isHvdManaged "For data period:" "For billing period:"}} {{this.formattedStartDate}} - {{this.formattedEndDate}} diff --git a/ui/app/components/clients/page-header.js b/ui/app/components/clients/page-header.js index 874c88969f..20204f28f6 100644 --- a/ui/app/components/clients/page-header.js +++ b/ui/app/components/clients/page-header.js @@ -32,6 +32,7 @@ import { task } from 'ember-concurrency'; */ export default class ClientsPageHeaderComponent extends Component { @service download; + @service flags; @service namespace; @service router; @service store; diff --git a/ui/app/components/clients/page/counts.hbs b/ui/app/components/clients/page/counts.hbs index 40add64d35..57f7bf1fc2 100644 --- a/ui/app/components/clients/page/counts.hbs +++ b/ui/app/components/clients/page/counts.hbs @@ -17,7 +17,7 @@

- This is the dashboard for your overall client count usages. Review Vault's + This is the dashboard for your overall client count usage. Review Vault's client counting documentation for more information. @@ -69,8 +69,12 @@ {{/if}} - {{#if this.version.isEnterprise}} + {{#if (and this.version.isEnterprise (not this.flags.isHvdManaged))}} {{! The "Client list" tab only renders for enterprise versions so there is no need for the nav bar }} + {{! The "Client list" tab is hidden on HVD managed clusters (for now) because the "Month" filter for that page + uses the `client_first_used_time` timestamp. This timestamp tracks when a client is FIRST seen in the queried date range (i.e. billing period). + This is useful for self-managed customers who are billed on monthly NEW clients, but not for HVD users who are billed on TOTAL clients per + month regardless of whether the client was seen in a previous month. }} {{/if}} diff --git a/ui/app/components/clients/page/counts.ts b/ui/app/components/clients/page/counts.ts index 5a9b19bfb9..260eb395c7 100644 --- a/ui/app/components/clients/page/counts.ts +++ b/ui/app/components/clients/page/counts.ts @@ -10,6 +10,7 @@ import { parseAPITimestamp } from 'core/utils/date-formatters'; import { filterVersionHistory } from 'core/utils/client-count-utils'; import type AdapterError from '@ember-data/adapter/error'; +import type FlagsService from 'vault/services/flags'; import type VersionService from 'vault/services/version'; import type ClientsActivityModel from 'vault/models/clients/activity'; import type ClientsConfigModel from 'vault/models/clients/config'; @@ -26,6 +27,7 @@ interface Args { } export default class ClientsCountsPageComponent extends Component { + @service declare readonly flags: FlagsService; @service declare readonly version: VersionService; get formattedStartDate() { diff --git a/ui/app/components/clients/page/overview.hbs b/ui/app/components/clients/page/overview.hbs index 1a899a0336..9e3d20a8ac 100644 --- a/ui/app/components/clients/page/overview.hbs +++ b/ui/app/components/clients/page/overview.hbs @@ -3,18 +3,12 @@ SPDX-License-Identifier: BUSL-1.1 }} - + {{! by_namespace is an empty array when there is no client count activity data }} {{#if @activity.byNamespace}} - + <:subheader> - Use the filters to - view the clients attributed by path. - { + @service declare readonly flags: FlagsService; + @tracked selectedMonth = ''; @cached - get byMonthNewClients() { + get byMonthClients() { + // HVD clusters are billed differently and the monthly total is the important metric. + if (this.flags.isHvdManaged) { + return this.args.activity.byMonth; + } + // For self-managed clusters only the new_clients per month are relevant because clients accumulate over a billing period. + // (Since "total" per month is not cumulative it's not a useful metric) return this.args.activity.byMonth?.map((m) => m?.new_clients) || []; } @@ -30,7 +40,7 @@ export default class ClientsOverviewPageComponent extends Component { // If no month is selected the table displays all of the activity for the queried date range. const selectedMonth = this.args.filterQueryParams.month; const namespaceData = selectedMonth - ? this.byMonthNewClients.find((m) => m.timestamp === selectedMonth)?.namespaces + ? this.byMonthClients.find((m) => m.timestamp === selectedMonth)?.namespaces : this.args.activity.byNamespace; // Get the array of "mounts" data nested in each namespace object and flatten @@ -39,7 +49,7 @@ export default class ClientsOverviewPageComponent extends Component { @cached get months() { - return this.byMonthNewClients.reverse().map((m) => m.timestamp); + return this.byMonthClients.map((m) => m.timestamp).reverse(); } get tableData() { diff --git a/ui/app/components/clients/running-total.hbs b/ui/app/components/clients/running-total.hbs index 519669e2f8..930cd30c91 100644 --- a/ui/app/components/clients/running-total.hbs +++ b/ui/app/components/clients/running-total.hbs @@ -3,16 +3,16 @@ SPDX-License-Identifier: BUSL-1.1 }} -{{#if @byMonthNewClients.length}} - +{{#if @byMonthClients.length}} + <:dataLeft> Client count and type distribution - + <:dataRight> @@ -37,7 +37,7 @@ {{/if}} @@ -45,7 +45,7 @@ {{else}} {{! Renders when viewing activity log data that predates the monthly breakdown added in 1.11 }} - + { @service declare readonly flags: FlagsService; + @service declare readonly version: VersionService; @tracked showStacked = false; get chartContainerText() { - return `The total clients in the specified date range, displayed per month. This includes entity, non-entity${ - this.flags.secretsSyncIsActivated ? ', ACME and secrets sync clients' : ' and ACME clients' - }. The total client count number is an important consideration for Vault billing.`; + const range = this.version.isEnterprise ? 'billing period' : 'date range'; + return this.flags.isHvdManaged + ? 'Number of total unique clients in the data period by client type, and total number of unique clients per month. The monthly total is the relevant billing metric.' + : `Number of clients in the ${range} by client type, and a breakdown of new clients per month during the ${range}. `; + } + + get dataKey() { + return this.flags.isHvdManaged ? 'clients' : 'new_clients'; } get runningTotalData() { - return this.args.byMonthNewClients.map((monthly) => ({ + // The parent component determines whether `monthly.clients` in @byMonthClients represents "new" or "total" clients per month. + // (We render "new" for self-managed clusters and "total" for HVD-managed.) + // As a result, we do not use `this.dataKey` to select a property from `monthly` but to add a superficial key + // to the data that ensures the chart tooltip and legend text render appropriately. + return this.args.byMonthClients.map((monthly) => ({ ...monthly, - new_clients: monthly.clients, + [this.dataKey]: monthly.clients, })); } @@ -54,6 +66,6 @@ export default class RunningTotal extends Component { ...(this.flags.secretsSyncIsActivated ? [{ key: 'secret_syncs', label: 'Secret sync clients' }] : []), ]; } - return [{ key: 'new_clients', label: 'New clients' }]; + return [{ key: this.dataKey, label: toLabel([this.dataKey]) }]; } } diff --git a/ui/app/components/clients/table.hbs b/ui/app/components/clients/table.hbs index eb5b1470c1..1d2d8c4dc7 100644 --- a/ui/app/components/clients/table.hbs +++ b/ui/app/components/clients/table.hbs @@ -25,7 +25,11 @@ - {{else if (and (eq key "clients") this.version.isEnterprise)}} + {{else if (and (eq key "clients") this.version.isEnterprise (not this.flags.isHvdManaged))}} + {{! The "Client list" tab is hidden on HVD managed clusters (for now) because the "Month" filter for that page + uses the `client_first_used_time` timestamp. This timestamp tracks when a client is FIRST seen in the queried date range (i.e. billing period). + This is useful for self-managed customers who are billed on monthly NEW clients, but not for HVD users who are billed on TOTAL clients per + month regardless of whether the client was seen in a previous month. }} { + @service declare readonly flags: FlagsService; @service declare readonly version: VersionService; @tracked currentPage = 1; diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index 1085096b8d..af72bc311a 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -78,7 +78,8 @@ export default class ClientsCountsRoute extends Route { async fetchAndFormatExportData(startTimestamp: string | undefined, endTimestamp: string | undefined) { // The "Client List" tab is only available on enterprise versions - if (this.version.isEnterprise) { + // For now, it is also hidden on HVD managed clusters + if (this.version.isEnterprise && !this.flags.isHvdManaged) { const adapter = this.store.adapterFor('clients/activity'); let exportData, exportError; try { diff --git a/ui/app/routes/vault/cluster/clients/counts/client-list.ts b/ui/app/routes/vault/cluster/clients/counts/client-list.ts index b98185737a..104edb7f11 100644 --- a/ui/app/routes/vault/cluster/clients/counts/client-list.ts +++ b/ui/app/routes/vault/cluster/clients/counts/client-list.ts @@ -4,5 +4,25 @@ */ import Route from '@ember/routing/route'; +import { service } from '@ember/service'; -export default class ClientsCountsClientListRoute extends Route {} +import type FlagsService from 'vault/services/flags'; +import type RouterService from '@ember/routing/router-service'; +import type VersionService from 'vault/services/version'; + +export default class ClientsCountsClientListRoute extends Route { + @service declare readonly flags: FlagsService; + @service declare readonly router: RouterService; + @service declare readonly version: VersionService; + + // The "Client list" tab is only available on enterprise versions + // The "Client list" tab is hidden on HVD managed clusters (for now) because the "Month" filter for that page + // uses the `client_first_used_time` timestamp. This timestamp tracks when a client is FIRST seen in the queried date range (i.e. billing period). + // This is useful for self-managed customers who are billed on monthly NEW clients, but not for HVD users who are billed on TOTAL clients per + // month regardless of whether the client was seen in a previous month. + redirect() { + if (this.version.isCommunity || this.flags.isHvdManaged) { + this.router.transitionTo('vault.cluster.clients.counts'); + } + } +} diff --git a/ui/app/styles/core/charts.scss b/ui/app/styles/core/charts.scss index aff6ae3d41..b9e09d8cdb 100644 --- a/ui/app/styles/core/charts.scss +++ b/ui/app/styles/core/charts.scss @@ -20,7 +20,7 @@ $fourth: #6cc5b0; border-radius: 50%; display: inline-block; } - .legend-dot-new_clients { + .legend-dot-total { background-color: $single; } // numbers are indices because chart legend is iterated over to ensure diff --git a/ui/tests/acceptance/clients/counts/client-list-test.js b/ui/tests/acceptance/clients/counts/client-list-test.js index 1b6814c933..74a4e01112 100644 --- a/ui/tests/acceptance/clients/counts/client-list-test.js +++ b/ui/tests/acceptance/clients/counts/client-list-test.js @@ -53,6 +53,24 @@ module('Acceptance | clients | counts | client list', function (hooks) { test('it hides client list tab on community', async function (assert) { this.version.type = 'community'; assert.dom(GENERAL.tab('client list')).doesNotExist(); + + // Navigate directly to URL to test redirect + await visit('/vault/clients/counts/client-list'); + assert.strictEqual(currentURL(), '/vault/clients/counts/overview', 'it redirects to overview'); + }); + + // skip this test on CE test runs because GET sys/license/features an enterprise only endpoint + test('enterprise: it hides client list tab on HVD managed clusters', async function (assert) { + this.owner.lookup('service:flags').featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + assert.dom(GENERAL.tab('client list')).doesNotExist(); + + // Navigate directly to URL to test redirect + await visit('/vault/clients/counts/client-list'); + assert.strictEqual( + currentURL(), + '/vault/clients/counts/overview?namespace=admin', + 'it redirects to overview' + ); }); test('it navigates to client list tab', async function (assert) { diff --git a/ui/tests/acceptance/clients/counts/overview-test.js b/ui/tests/acceptance/clients/counts/overview-test.js index 2bb540294e..10e6981703 100644 --- a/ui/tests/acceptance/clients/counts/overview-test.js +++ b/ui/tests/acceptance/clients/counts/overview-test.js @@ -67,10 +67,10 @@ module('Acceptance | clients | overview', function (hooks) { .dom(CLIENT_COUNT.dateRange.dateDisplay('end')) .hasText('January 2024', 'end month is correctly parsed from STATIC_NOW'); assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) + .dom(CLIENT_COUNT.card('Client usage trends')) .exists('Shows running totals with monthly breakdown charts'); assert - .dom(`${CLIENT_COUNT.card('Client usage trends for selected billing period')} ${CHARTS.xAxisLabel}`) + .dom(`${CLIENT_COUNT.card('Client usage trends')} ${CHARTS.xAxisLabel}`) .hasText('7/23', 'x-axis labels start with billing start date'); assert.dom(CHARTS.xAxisLabel).exists({ count: 7 }, 'chart months matches query'); }); @@ -95,7 +95,7 @@ module('Acceptance | clients | overview', function (hooks) { .dom(CLIENT_COUNT.usageStats('Client usage')) .exists('running total single month usage stats show'); assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) + .dom(CLIENT_COUNT.card('Client usage trends')) .doesNotExist('running total month over month charts do not show'); // change to start on month/year of upgrade to 1.10 @@ -108,10 +108,10 @@ module('Acceptance | clients | overview', function (hooks) { .dom(CLIENT_COUNT.dateRange.dateDisplay('start')) .hasText('September 2023', 'client count start month is correctly parsed from start query'); assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) + .dom(CLIENT_COUNT.card('Client usage trends')) .exists('Shows running totals with monthly breakdown charts'); assert - .dom(`${CLIENT_COUNT.card('Client usage trends for selected billing period')} ${CHARTS.xAxisLabel}`) + .dom(`${CLIENT_COUNT.card('Client usage trends')} ${CHARTS.xAxisLabel}`) .hasText('9/23', 'x-axis labels start with queried start month (upgrade date)'); assert.dom(CHARTS.xAxisLabel).exists({ count: 4 }, 'chart months matches query'); @@ -121,7 +121,7 @@ module('Acceptance | clients | overview', function (hooks) { await fillIn(CLIENT_COUNT.dateRange.editDate('end'), upgradeMonth); await click(GENERAL.submitButton); assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) + .dom(CLIENT_COUNT.card('Client usage trends')) .exists('running total month over month charts show'); // query historical date range (from September 2023 to December 2023) @@ -137,7 +137,7 @@ module('Acceptance | clients | overview', function (hooks) { .dom(CLIENT_COUNT.dateRange.dateDisplay('end')) .hasText('December 2023', 'it displays correct end time'); assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) + .dom(CLIENT_COUNT.card('Client usage trends')) .exists('Shows running totals with monthly breakdown charts'); assert.dom(CHARTS.xAxisLabel).exists({ count: 4 }, 'chart months matches query'); @@ -154,6 +154,14 @@ module('Acceptance | clients | overview', function (hooks) { }); }); + test('it does not render client list links for HVD managed clusters', async function (assert) { + this.owner.lookup('service:flags').featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + + assert + .dom(`${GENERAL.tableData(0, 'clients')} a`) + .doesNotExist('client counts do not render as hyperlinks'); + }); + // * FILTERING ASSERTIONS // These tests use the static data from the ACTIVITY_RESPONSE_STUB to assert filtering // Filtering tests are split between integration and acceptance tests @@ -336,21 +344,5 @@ module('Acceptance | clients | overview', function (hooks) { 'it renders legend in order that matches the stacked bar data and does not include secret sync' ); }); - - test('it should show secrets sync stats for HVD managed clusters', async function (assert) { - // mock HVD managed cluster - this.owner.lookup('service:flags').featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; - - await login(); - await visit('/vault/clients/counts/overview'); - assert.dom(CLIENT_COUNT.statLegendValue('Secret sync clients')).exists(); - await click(GENERAL.inputByAttr('toggle view')); - assert - .dom(CHARTS.legend) - .hasText( - 'Entity clients Non-entity clients ACME clients Secret sync clients', - 'it renders legend in order that matches the stacked bar data' - ); - }); }); }); diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js index 8b826a6d39..fb267af3a0 100644 --- a/ui/tests/integration/components/clients/date-range-test.js +++ b/ui/tests/integration/components/clients/date-range-test.js @@ -109,7 +109,7 @@ module('Integration | Component | clients/date-range', function (hooks) { this.billingStartTime = '2018-01-01T14:15:30'; }); - test('it billing start date dropdown for enterprise', async function (assert) { + test('it renders billing start date dropdown for enterprise', async function (assert) { await this.renderComponent(); await click(DATE_RANGE.edit); const expectedPeriods = [ @@ -125,5 +125,32 @@ module('Integration | Component | clients/date-range', function (hooks) { assert.dom(item).hasText(month, `dropdown index: ${idx} renders ${month}`); }); }); + + test('it updates toggle text when a new date is selected', async function (assert) { + this.onChange = ({ start_time }) => this.set('startTimestamp', start_time); + + await this.renderComponent(); + assert.dom(DATE_RANGE.edit).hasText('January 2018').hasAttribute('aria-expanded', 'false'); + await click(DATE_RANGE.edit); + assert.dom(DATE_RANGE.edit).hasAttribute('aria-expanded', 'true'); + await click(DATE_RANGE.dropdownOption(1)); + assert + .dom(DATE_RANGE.edit) + .hasText('January 2017') + .hasAttribute('aria-expanded', 'false', 'it closes dropdown after selection'); + }); + + test('it renders billing period text', async function (assert) { + await this.renderComponent(); + assert + .dom(this.element) + .hasText('Change billing period January 2018', 'it renders billing related text'); + }); + + test('it renders data period text for HVD managed clusters', async function (assert) { + this.owner.lookup('service:flags').featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + await this.renderComponent(); + assert.dom(this.element).hasText('Change data period January 2018'); + }); }); }); diff --git a/ui/tests/integration/components/clients/page-header-test.js b/ui/tests/integration/components/clients/page-header-test.js index 2e69787a2b..5928196217 100644 --- a/ui/tests/integration/components/clients/page-header-test.js +++ b/ui/tests/integration/components/clients/page-header-test.js @@ -34,6 +34,7 @@ module('Integration | Component | clients/page-header', function (hooks) { this.renderComponent = async () => { return render(hbs` ns.label === 'ns1/') + .mounts.find((m) => m.label === 'auth/userpass/0/'); + + await this.renderComponent(); + assert.dom(CHARTS.legend).hasText('New clients'); + assert + .dom(GENERAL.tableData(0, 'clients')) + .hasText(`${topMount.clients}`, 'table renders total monthly clients'); + }); + test('it filters data if @filterQueryParams specify a month', async function (assert) { const filterKey = 'month'; const filterValue = this.mostRecentMonth.timestamp; @@ -209,4 +224,20 @@ module('Integration | Component | clients/page/overview', function (hooks) { .dom(CLIENT_COUNT.card('table empty state')) .hasText('No data found Clear or change filters to view client count data. Client count documentation'); }); + + test('it renders TOTAL monthly clients for HVD instead of new clients', async function (assert) { + this.owner.lookup('service:flags').featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + this.filterQueryParams = { + month: this.mostRecentMonth.timestamp, + }; + const topMount = this.mostRecentMonth.namespaces + .find((ns) => ns.label === 'root') + .mounts.find((m) => m.label === 'acme/pki/0/'); + + await this.renderComponent(); + assert.dom(CHARTS.legend).hasText('Clients'); + assert + .dom(GENERAL.tableData(0, 'clients')) + .hasText(`${topMount.clients}`, 'table renders total monthly clients'); + }); }); diff --git a/ui/tests/integration/components/clients/running-total-test.js b/ui/tests/integration/components/clients/running-total-test.js index 54270ed7e7..662e3ae983 100644 --- a/ui/tests/integration/components/clients/running-total-test.js +++ b/ui/tests/integration/components/clients/running-total-test.js @@ -26,6 +26,7 @@ module('Integration | Component | clients/running-total', function (hooks) { hooks.beforeEach(async function () { this.flags = this.owner.lookup('service:flags'); + this.version = this.owner.lookup('service:version'); this.flags.activatedFlags = ['secrets-sync']; sinon.replace(timestamp, 'now', sinon.fake.returns(STATIC_NOW)); clientsHandler(this.server); @@ -35,24 +36,53 @@ module('Integration | Component | clients/running-total', function (hooks) { end_time: { timestamp: getUnixTime(timestamp.now()) }, }; this.activity = await store.queryRecord('clients/activity', activityQuery); - this.byMonthNewClients = this.activity.byMonth.map((d) => d.new_clients); + this.byMonthClients = this.activity.byMonth.map((d) => d.new_clients); this.renderComponent = async () => { await render(hbs` `); }; }); + test('it text for community versions', async function (assert) { + this.version.type = 'community'; + await this.renderComponent(); + assert + .dom(`${CLIENT_COUNT.card('Client usage trends')} p`) + .hasText( + 'Number of clients in the date range by client type, and a breakdown of new clients per month during the date range.' + ); + }); + + // abbreviating "ent" so the test is not filtered out in CE repo runs + test('it renders text for ent versions', async function (assert) { + this.version.type = 'enterprise'; + await this.renderComponent(); + assert + .dom(`${CLIENT_COUNT.card('Client usage trends')} p`) + .hasText( + 'Number of clients in the billing period by client type, and a breakdown of new clients per month during the billing period.' + ); + }); + + test('it renders text for HVD managed versions', async function (assert) { + this.flags.featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE']; + await this.renderComponent(); + assert + .dom(`${CLIENT_COUNT.card('Client usage trends')} p`) + .hasText( + 'Number of total unique clients in the data period by client type, and total number of unique clients per month. The monthly total is the relevant billing metric.' + ); + }); + test('it renders with full monthly activity data', async function (assert) { await this.renderComponent(); - assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) - .exists('running total component renders'); + assert.dom(CLIENT_COUNT.card('Client usage trends')).exists('running total component renders'); assert.dom(CHARTS.chart('Client usage by month')).exists('bar chart renders'); assert.dom(CHARTS.legend).hasText('New clients'); const expectedColor = 'rgb(28, 52, 95)'; @@ -76,22 +106,20 @@ module('Integration | Component | clients/running-total', function (hooks) { // assert bar chart is correct findAll(CHARTS.xAxisLabel).forEach((e, i) => { - const timestamp = this.byMonthNewClients[i].timestamp; + const timestamp = this.byMonthClients[i].timestamp; const displayMonth = parseAPITimestamp(timestamp, 'M/yy'); assert.dom(e).hasText(displayMonth, `renders x-axis labels for bar chart: ${displayMonth}`); }); assert .dom(CHARTS.verticalBar) - .exists({ count: this.byMonthNewClients.length }, 'renders correct number of bars '); + .exists({ count: this.byMonthClients.length }, 'renders correct number of bars '); }); test('it toggles to split chart by client type', async function (assert) { await this.renderComponent(); await click(GENERAL.inputByAttr('toggle view')); - assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) - .exists('running total component renders'); + assert.dom(CLIENT_COUNT.card('Client usage trends')).exists('running total component renders'); assert.dom(CHARTS.chart('Client usage by month')).exists('bar chart renders'); assert .dom(CHARTS.legend) @@ -117,12 +145,12 @@ module('Integration | Component | clients/running-total', function (hooks) { // assert bar chart is correct findAll(CHARTS.xAxisLabel).forEach((e, i) => { - const timestamp = this.byMonthNewClients[i].timestamp; + const timestamp = this.byMonthClients[i].timestamp; const displayMonth = parseAPITimestamp(timestamp, 'M/yy'); assert.dom(e).hasText(`${displayMonth}`, `renders x-axis labels for bar chart: ${displayMonth}`); }); - const months = this.byMonthNewClients.length; + const months = this.byMonthClients.length; const barsPerMonth = expectedLegend.length; assert .dom(CHARTS.verticalBar) @@ -130,7 +158,7 @@ module('Integration | Component | clients/running-total', function (hooks) { }); test('it renders when no monthly breakdown is available', async function (assert) { - this.byMonthNewClients = []; + this.byMonthClients = []; await this.renderComponent(); const expectedStats = { Entity: formatNumber([this.activity.total.entity_clients]), @@ -153,13 +181,11 @@ module('Integration | Component | clients/running-total', function (hooks) { test('it hides secret sync totals when feature is not activated', async function (assert) { this.flags.activatedFlags = []; // reset secret sync clients to 0 - this.byMonthNewClients = this.byMonthNewClients.map((obj) => ({ ...obj, secret_syncs: 0 })); + this.byMonthClients = this.byMonthClients.map((obj) => ({ ...obj, secret_syncs: 0 })); await this.renderComponent(); - assert - .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) - .exists('running total component renders'); + assert.dom(CLIENT_COUNT.card('Client usage trends')).exists('running total component renders'); assert.dom(CHARTS.chart('Client usage by month')).exists('bar chart renders'); assert.dom(CLIENT_COUNT.statLegendValue('Entity clients')).exists(); assert.dom(CLIENT_COUNT.statLegendValue('Non-entity clients')).exists(); @@ -187,7 +213,7 @@ module('Integration | Component | clients/running-total', function (hooks) { assert.strictEqual(dotColor, color, `${label} - actual color: ${dotColor}, expected: ${color}`); }); - const months = this.byMonthNewClients.length; + const months = this.byMonthClients.length; const barsPerMonth = expectedLegend.length; assert .dom(CHARTS.verticalBar)