-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
UI: [VAULT-17041] Client count card (#22323)
Co-authored-by: Angel Garbarino <[email protected]>
- Loading branch information
1 parent
49ecc8b
commit 95d415a
Showing
8 changed files
with
305 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: MPL-2.0 | ||
*/ | ||
|
||
import Component from '@glimmer/component'; | ||
import getStorage from 'vault/lib/token-storage'; | ||
import timestamp from 'core/utils/timestamp'; | ||
import { task } from 'ember-concurrency'; | ||
import { waitFor } from '@ember/test-waiters'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { inject as service } from '@ember/service'; | ||
|
||
/** | ||
* @module DashboardClientCountCard | ||
* DashboardClientCountCard component are used to display total and new client count information | ||
* | ||
* @example | ||
* ```js | ||
* <Dashboard::ClientCountCard @license={{@model.license}} /> | ||
* ``` | ||
* @param {object} license - license object passed from the parent | ||
*/ | ||
|
||
export default class DashboardClientCountCard extends Component { | ||
@service store; | ||
|
||
@tracked activityData = null; | ||
@tracked clientConfig = null; | ||
@tracked updatedAt = timestamp.now().toISOString(); | ||
|
||
constructor() { | ||
super(...arguments); | ||
this.fetchClientActivity.perform(); | ||
this.clientConfig = this.store.queryRecord('clients/config', {}).catch(() => {}); | ||
} | ||
|
||
get currentMonthActivityTotalCount() { | ||
return this.activityData?.byMonth?.lastObject?.new_clients.clients; | ||
} | ||
|
||
get licenseStartTime() { | ||
return this.args.license.startTime || getStorage().getItem('vault:ui-inputted-start-date') || null; | ||
} | ||
|
||
@task | ||
@waitFor | ||
*fetchClientActivity() { | ||
this.updatedAt = timestamp.now().toISOString(); | ||
// only make the network request if we have a start_time | ||
if (!this.licenseStartTime) return {}; | ||
try { | ||
this.activityData = yield this.store.queryRecord('clients/activity', { | ||
start_time: { timestamp: this.licenseStartTime }, | ||
end_time: { timestamp: this.updatedAt }, | ||
}); | ||
this.noActivityData = this.activityData.activity.id === 'no-data' ? true : false; | ||
} catch (error) { | ||
this.error = error; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{{#if (eq @config.enabled "On")}} | ||
<EmptyState | ||
@title="No data received {{if @dateRangeMessage @dateRangeMessage}}" | ||
@message="Tracking is turned on and Vault is gathering data. It should appear here within 30 minutes." | ||
/> | ||
{{else}} | ||
<EmptyState | ||
@title="Data tracking is disabled" | ||
@message="Tracking is disabled, and no data is being collected. To turn it on, edit the configuration." | ||
> | ||
{{#if @config.canEdit}} | ||
<p> | ||
<LinkTo @route="vault.cluster.clients.config"> | ||
Go to configuration | ||
</LinkTo> | ||
</p> | ||
{{/if}} | ||
</EmptyState> | ||
{{/if}} |
60 changes: 60 additions & 0 deletions
60
ui/app/templates/components/dashboard/client-count-card.hbs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<div class="is-flex-between"> | ||
<h3 class="title is-4 has-bottom-margin-xxs" data-test-client-count-title> | ||
Client count | ||
</h3> | ||
|
||
<div> | ||
<Hds::Link::Inline @route="vault.cluster.clients.dashboard" class="is-no-underline">Details</Hds::Link::Inline> | ||
</div> | ||
</div> | ||
|
||
<hr class="has-background-gray-100" /> | ||
|
||
{{#if this.noActivityData}} | ||
{{! This will likely not be show since the client activity api was changed to always return data. In the past it | ||
would return no activity data. Adding this empty state here to match the current client count behavior }} | ||
<Clients::NoData @config={{this.clientConfig}} /> | ||
{{else}} | ||
<div class="is-grid grid-2-columns grid-gap-2 has-top-margin-m grid-align-items-start is-flex-v-centered"> | ||
{{#if this.fetchClientActivity.isRunning}} | ||
<VaultLogoSpinner /> | ||
{{else}} | ||
<StatText | ||
@label="Total" | ||
@value={{this.activityData.total.clients}} | ||
@size="l" | ||
@subText="The number of clients in this billing period ({{date-format | ||
this.licenseStartTime | ||
'MMM yyyy' | ||
}} - {{date-format this.updatedAt 'MMM yyyy'}})." | ||
data-test-stat-text="total-clients" | ||
/> | ||
<StatText | ||
@label="New" | ||
@value={{this.currentMonthActivityTotalCount}} | ||
@size="l" | ||
@subText="The number of clients new to Vault in the current month." | ||
data-test-stat-text="new-clients" | ||
/> | ||
{{/if}} | ||
</div> | ||
|
||
{{#unless this.fetchClientActivity.isRunning}} | ||
<div class="has-top-margin-l is-flex-center"> | ||
<Hds::Button | ||
@text="Refresh" | ||
@isIconOnly={{true}} | ||
@color="tertiary" | ||
@icon="sync" | ||
disabled={{this.fetchClientActivity.isRunning}} | ||
class="has-padding-xxs" | ||
{{on "click" (perform this.fetchClientActivity)}} | ||
data-test-refresh | ||
/> | ||
<small class="has-left-margin-xs has-text-grey"> | ||
Updated | ||
{{date-format this.updatedAt "MMM dd, yyyy HH:mm:SS"}} | ||
</small> | ||
</div> | ||
{{/unless}} | ||
{{/if}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
142 changes: 142 additions & 0 deletions
142
ui/tests/integration/components/dashboard/client-count-card-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: MPL-2.0 | ||
*/ | ||
|
||
import { module, test } from 'qunit'; | ||
import { setupRenderingTest } from 'vault/tests/helpers'; | ||
import { render, click } from '@ember/test-helpers'; | ||
import { hbs } from 'ember-cli-htmlbars'; | ||
import { setupMirage } from 'ember-cli-mirage/test-support'; | ||
import timestamp from 'core/utils/timestamp'; | ||
import { parseAPITimestamp } from 'core/utils/date-formatters'; | ||
|
||
module('Integration | Component | dashboard/client-count-card', function (hooks) { | ||
setupRenderingTest(hooks); | ||
setupMirage(hooks); | ||
|
||
hooks.beforeEach(function () { | ||
this.license = { | ||
startTime: '2018-04-03T14:15:30', | ||
}; | ||
}); | ||
|
||
test('it should display client count information', async function (assert) { | ||
this.server.get('sys/internal/counters/activity', () => { | ||
return { | ||
request_id: 'some-activity-id', | ||
data: { | ||
months: [ | ||
{ | ||
timestamp: '2023-08-01T00:00:00-07:00', | ||
counts: {}, | ||
namespaces: [ | ||
{ | ||
namespace_id: 'root', | ||
namespace_path: '', | ||
counts: {}, | ||
mounts: [{ mount_path: 'auth/up2/', counts: {} }], | ||
}, | ||
], | ||
new_clients: { | ||
counts: { | ||
clients: 12, | ||
}, | ||
namespaces: [ | ||
{ | ||
namespace_id: 'root', | ||
namespace_path: '', | ||
counts: { | ||
clients: 12, | ||
}, | ||
mounts: [{ mount_path: 'auth/up2/', counts: {} }], | ||
}, | ||
], | ||
}, | ||
}, | ||
], | ||
total: { | ||
clients: 300417, | ||
entity_clients: 73150, | ||
non_entity_clients: 227267, | ||
}, | ||
}, | ||
}; | ||
}); | ||
|
||
await render(hbs`<Dashboard::ClientCountCard @license={{this.license}} />`); | ||
assert.dom('[data-test-client-count-title]').hasText('Client count'); | ||
assert.dom('[data-test-stat-text="total-clients"] .stat-label').hasText('Total'); | ||
assert | ||
.dom('[data-test-stat-text="total-clients"] .stat-text') | ||
.hasText( | ||
`The number of clients in this billing period (Apr 2018 - ${parseAPITimestamp( | ||
timestamp.now().toISOString(), | ||
'MMM yyyy' | ||
)}).` | ||
); | ||
assert.dom('[data-test-stat-text="total-clients"] .stat-value').hasText('300,417'); | ||
assert.dom('[data-test-stat-text="new-clients"] .stat-label').hasText('New'); | ||
assert | ||
.dom('[data-test-stat-text="new-clients"] .stat-text') | ||
.hasText('The number of clients new to Vault in the current month.'); | ||
assert.dom('[data-test-stat-text="new-clients"] .stat-value').hasText('12'); | ||
this.server.get('sys/internal/counters/activity', () => { | ||
return { | ||
request_id: 'some-activity-id', | ||
data: { | ||
months: [ | ||
{ | ||
timestamp: '2023-09-01T00:00:00-07:00', | ||
counts: {}, | ||
namespaces: [ | ||
{ | ||
namespace_id: 'root', | ||
namespace_path: '', | ||
counts: {}, | ||
mounts: [{ mount_path: 'auth/up2/', counts: {} }], | ||
}, | ||
], | ||
new_clients: { | ||
counts: { | ||
clients: 5, | ||
}, | ||
namespaces: [ | ||
{ | ||
namespace_id: 'root', | ||
namespace_path: '', | ||
counts: { | ||
clients: 12, | ||
}, | ||
mounts: [{ mount_path: 'auth/up2/', counts: {} }], | ||
}, | ||
], | ||
}, | ||
}, | ||
], | ||
total: { | ||
clients: 120, | ||
entity_clients: 100, | ||
non_entity_clients: 100, | ||
}, | ||
}, | ||
}; | ||
}); | ||
await click('[data-test-refresh]'); | ||
assert.dom('[data-test-stat-text="total-clients"] .stat-label').hasText('Total'); | ||
assert | ||
.dom('[data-test-stat-text="total-clients"] .stat-text') | ||
.hasText( | ||
`The number of clients in this billing period (Apr 2018 - ${parseAPITimestamp( | ||
timestamp.now().toISOString(), | ||
'MMM yyyy' | ||
)}).` | ||
); | ||
assert.dom('[data-test-stat-text="total-clients"] .stat-value').hasText('120'); | ||
assert.dom('[data-test-stat-text="new-clients"] .stat-label').hasText('New'); | ||
assert | ||
.dom('[data-test-stat-text="new-clients"] .stat-text') | ||
.hasText('The number of clients new to Vault in the current month.'); | ||
assert.dom('[data-test-stat-text="new-clients"] .stat-value').hasText('5'); | ||
}); | ||
}); |