mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-26 05:01:11 +01:00
UI: Allow fallback date querying for client usage when no billing timestamp is provided (#10728) (#10755)
* some change * add console logs to debug rendering error * add fallback in case billingStartTime does not exist * remove console logs * allow querying dates if there is no billing start timestamp * add comments and test coverage * add changelog * remove extra divs, small copy changes * remove extra line Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
This commit is contained in:
parent
6e60eb3ff6
commit
4ab8b902f2
3
changelog/_10728.txt
Normal file
3
changelog/_10728.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
```release-note:improvement
|
||||||
|
ui/activity (enterprise): Allow manual querying of client usage if there is a problem retrieving the license start time.
|
||||||
|
```
|
||||||
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
<div ...attributes>
|
<div ...attributes>
|
||||||
<div class="is-flex-column align-items-end">
|
<div class="is-flex-column align-items-end">
|
||||||
{{#if this.version.isEnterprise}}
|
{{! Enterprise should always have a @billingStartTime but as a fallback allow the user to query dates manually }}
|
||||||
|
{{#if (and @billingStartTime this.version.isEnterprise)}}
|
||||||
<Hds::Text::Display @tag="p" @size="100" class="has-bottom-margin-xs">
|
<Hds::Text::Display @tag="p" @size="100" class="has-bottom-margin-xs">
|
||||||
{{if this.flags.isHvdManaged "Change data period" "Change billing period"}}
|
{{if this.flags.isHvdManaged "Change data period" "Change billing period"}}
|
||||||
</Hds::Text::Display>
|
</Hds::Text::Display>
|
||||||
@ -34,9 +35,8 @@
|
|||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</Hds::Dropdown>
|
</Hds::Dropdown>
|
||||||
{{else}}
|
{{else if (not (and @startTimestamp @endTimestamp))}}
|
||||||
{{! Hide if the user has made an initial query because dates can be updated by clicking "Edit" beside the date range }}
|
{{! Hide if the user has already made an initial query because dates can be updated by clicking "Edit" beside the date range }}
|
||||||
{{#unless (and @startTimestamp @endTimestamp)}}
|
|
||||||
<Hds::Button
|
<Hds::Button
|
||||||
class="has-left-margin-xs"
|
class="has-left-margin-xs"
|
||||||
@text="Set date range"
|
@text="Set date range"
|
||||||
@ -44,7 +44,6 @@
|
|||||||
{{on "click" (fn @setEditModalVisible true)}}
|
{{on "click" (fn @setEditModalVisible true)}}
|
||||||
data-test-date-range-edit
|
data-test-date-range-edit
|
||||||
/>
|
/>
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -54,10 +53,11 @@
|
|||||||
Edit date range
|
Edit date range
|
||||||
</M.Header>
|
</M.Header>
|
||||||
<M.Body>
|
<M.Body>
|
||||||
<Hds::Text::Body @tag="div">
|
{{#if this.version.isCommunity}}
|
||||||
<p class="has-bottom-margin-s">
|
<p class="has-bottom-margin-s">
|
||||||
Use custom date ranges to query historic client count data. Query results do not include the current month.
|
Use custom date ranges to query historic client count data. Query results do not include the current month.
|
||||||
</p>
|
</p>
|
||||||
|
{{/if}}
|
||||||
<ul class="has-bottom-margin-s">
|
<ul class="has-bottom-margin-s">
|
||||||
<li>
|
<li>
|
||||||
<strong>Start</strong>
|
<strong>Start</strong>
|
||||||
@ -71,14 +71,13 @@
|
|||||||
<p class="has-bottom-margin-s">
|
<p class="has-bottom-margin-s">
|
||||||
We recommend setting
|
We recommend setting
|
||||||
<strong>Start</strong>
|
<strong>Start</strong>
|
||||||
to your Vault deploy date to get the most accurate new and total client count estimations.</p>
|
to your Vault deploy date to get the most accurate client counts.</p>
|
||||||
|
|
||||||
<div class="clients-date-range-display">
|
<div class="clients-date-range-display">
|
||||||
<div>
|
<div>
|
||||||
<Hds::Form::TextInput::Field
|
<Hds::Form::TextInput::Field
|
||||||
@type="month"
|
@type="month"
|
||||||
@value={{this.modalStart}}
|
@value={{this.modalStart}}
|
||||||
max={{this.previousMonth}}
|
max={{if this.version.isCommunity this.previousMonth (date-format this.currentMonth "yyyy-MM")}}
|
||||||
id="start-month"
|
id="start-month"
|
||||||
name="modalStart"
|
name="modalStart"
|
||||||
{{on "change" this.updateDate}}
|
{{on "change" this.updateDate}}
|
||||||
@ -92,7 +91,7 @@
|
|||||||
<Hds::Form::TextInput::Field
|
<Hds::Form::TextInput::Field
|
||||||
@type="month"
|
@type="month"
|
||||||
@value={{this.modalEnd}}
|
@value={{this.modalEnd}}
|
||||||
max={{this.previousMonth}}
|
max={{if this.version.isCommunity this.previousMonth (date-format this.currentMonth "yyyy-MM")}}
|
||||||
id="end-month"
|
id="end-month"
|
||||||
name="modalEnd"
|
name="modalEnd"
|
||||||
{{on "change" this.updateDate}}
|
{{on "change" this.updateDate}}
|
||||||
@ -109,7 +108,6 @@
|
|||||||
data-test-date-range-validation
|
data-test-date-range-validation
|
||||||
>{{this.validationError}}</Hds::Form::Error>
|
>{{this.validationError}}</Hds::Form::Error>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</Hds::Text::Body>
|
|
||||||
</M.Body>
|
</M.Body>
|
||||||
<M.Footer as |F|>
|
<M.Footer as |F|>
|
||||||
<Hds::Button @text="Save" {{on "click" this.handleSave}} data-test-submit />
|
<Hds::Button @text="Save" {{on "click" this.handleSave}} data-test-submit />
|
||||||
|
|||||||
@ -86,7 +86,8 @@ export default class ClientsDateRangeComponent extends Component<Args> {
|
|||||||
if (this.modalStart > this.modalEnd) {
|
if (this.modalStart > this.modalEnd) {
|
||||||
return 'Start date must be before end date.';
|
return 'Start date must be before end date.';
|
||||||
}
|
}
|
||||||
if (this.modalStart > this.previousMonth || this.modalEnd > this.previousMonth) {
|
const isCurrentMonth = this.modalStart > this.previousMonth || this.modalEnd > this.previousMonth;
|
||||||
|
if (this.version.isCommunity && isCurrentMonth) {
|
||||||
return 'You cannot select the current month or beyond.';
|
return 'You cannot select the current month or beyond.';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -23,22 +23,21 @@
|
|||||||
/>
|
/>
|
||||||
</PH.Subtitle>
|
</PH.Subtitle>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.version.isEnterprise}}
|
<PH.Description>
|
||||||
<PH.Description class="has-text-weight-semibold flex">
|
{{#if (and this.version.isEnterprise @billingStartTime)}}
|
||||||
<p>
|
{{! Enterprise should always have a @billingStartTime but as a fallback allow the user to query dates manually. }}
|
||||||
|
{{! A dropdown renders in Clients::DateRange to query specific dates if we have a @billingStartTime }}
|
||||||
|
<p class="has-text-weight-semibold">
|
||||||
{{if this.flags.isHvdManaged "For data period: " "For billing period: "}}
|
{{if this.flags.isHvdManaged "For data period: " "For billing period: "}}
|
||||||
<span data-test-date-range="start">{{this.formattedStartDate}}</span>
|
<span data-test-date-range="start">{{this.formattedStartDate}}</span>
|
||||||
-
|
-
|
||||||
<span data-test-date-range="end">{{this.formattedEndDate}}</span>
|
<span data-test-date-range="end">{{this.formattedEndDate}}</span>
|
||||||
</p>
|
</p>
|
||||||
</PH.Description>
|
{{else if (and @startTimestamp @endTimestamp)}}
|
||||||
{{/if}}
|
{{! If these timestamps exist an initial query has already been made using the modal in Clients::DateRange }}
|
||||||
<PH.Description>
|
|
||||||
{{#if this.showCommunity}}
|
|
||||||
<div class="has-top-padding-m">
|
<div class="has-top-padding-m">
|
||||||
<Hds::Text::Display @tag="h3">Client counting period</Hds::Text::Display>
|
<Hds::Text::Display @tag="h3">Client counting period</Hds::Text::Display>
|
||||||
<Hds::Text::Body @tag="p" class="has-text-grey">Specify a date range to view within that timeframe. Click 'Edit' to
|
<Hds::Text::Body @tag="p" @size="100" color="faint">Click 'Edit' to select a different date range.</Hds::Text::Body>
|
||||||
choose a different date range.</Hds::Text::Body>
|
|
||||||
<Hds::Text::Body @tag="p">
|
<Hds::Text::Body @tag="p">
|
||||||
<span data-test-date-range="start">{{this.formattedStartDate}}</span>
|
<span data-test-date-range="start">{{this.formattedStartDate}}</span>
|
||||||
-
|
-
|
||||||
|
|||||||
@ -95,10 +95,6 @@ export default class ClientsPageHeaderComponent extends Component {
|
|||||||
return `clients_export${ns}${csvDateRange}`;
|
return `clients_export${ns}${csvDateRange}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get showCommunity() {
|
|
||||||
return this.version.isCommunity && !!this.formattedStartDate && !!this.formattedEndDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getExportData() {
|
async getExportData() {
|
||||||
const adapter = this.store.adapterFor('clients/activity');
|
const adapter = this.store.adapterFor('clients/activity');
|
||||||
const { startTimestamp, endTimestamp } = this.args;
|
const { startTimestamp, endTimestamp } = this.args;
|
||||||
|
|||||||
@ -39,8 +39,11 @@ export default class ClientsCountsPageComponent extends Component<Args> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get formattedBillingStartDate() {
|
get formattedBillingStartDate() {
|
||||||
|
if (this.args.config?.billingStartTimestamp) {
|
||||||
return this.args.config.billingStartTimestamp.toISOString();
|
return this.args.config.billingStartTimestamp.toISOString();
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// passed into page-header for the export modal alert
|
// passed into page-header for the export modal alert
|
||||||
get upgradesDuringActivity() {
|
get upgradesDuringActivity() {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ export default class ClientsOverviewPageComponent extends Component<Args> {
|
|||||||
get byMonthClients() {
|
get byMonthClients() {
|
||||||
// HVD clusters are billed differently and the monthly total is the important metric.
|
// HVD clusters are billed differently and the monthly total is the important metric.
|
||||||
if (this.flags.isHvdManaged) {
|
if (this.flags.isHvdManaged) {
|
||||||
return this.args.activity.byMonth;
|
return this.args.activity.byMonth || [];
|
||||||
}
|
}
|
||||||
// For self-managed clusters only the new_clients per month are relevant because clients accumulate over a billing period.
|
// 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)
|
// (Since "total" per month is not cumulative it's not a useful metric)
|
||||||
@ -36,6 +36,7 @@ export default class ClientsOverviewPageComponent extends Component<Args> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Supplies data passed to dropdown filters (except months which is computed below )
|
// Supplies data passed to dropdown filters (except months which is computed below )
|
||||||
|
@cached
|
||||||
get activityData() {
|
get activityData() {
|
||||||
// If no month is selected the table displays all of the activity for the queried date range.
|
// If no month is selected the table displays all of the activity for the queried date range.
|
||||||
const selectedMonth = this.args.filterQueryParams.month;
|
const selectedMonth = this.args.filterQueryParams.month;
|
||||||
@ -49,9 +50,12 @@ export default class ClientsOverviewPageComponent extends Component<Args> {
|
|||||||
|
|
||||||
@cached
|
@cached
|
||||||
get months() {
|
get months() {
|
||||||
return this.byMonthClients.map((m) => m.timestamp).reverse();
|
const timestamps = this.byMonthClients.map((m) => m.timestamp);
|
||||||
|
// display the most recent month at the top of the dropdown
|
||||||
|
return timestamps.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@cached
|
||||||
get tableData() {
|
get tableData() {
|
||||||
if (this.activityData?.length) {
|
if (this.activityData?.length) {
|
||||||
// Reset the `month` query param because it determines which dataset (see this.activityData)
|
// Reset the `month` query param because it determines which dataset (see this.activityData)
|
||||||
|
|||||||
@ -18,6 +18,7 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||||||
setupRenderingTest(hooks);
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
hooks.beforeEach(function () {
|
hooks.beforeEach(function () {
|
||||||
|
this.version = this.owner.lookup('service:version');
|
||||||
Sinon.replace(timestamp, 'now', Sinon.fake.returns(new Date('2018-04-03T14:15:30')));
|
Sinon.replace(timestamp, 'now', Sinon.fake.returns(new Date('2018-04-03T14:15:30')));
|
||||||
this.now = timestamp.now();
|
this.now = timestamp.now();
|
||||||
this.startTimestamp = '2018-01-01T14:15:30';
|
this.startTimestamp = '2018-01-01T14:15:30';
|
||||||
@ -36,6 +37,11 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it does not render if a start and end timestamp are already provided', async function (assert) {
|
||||||
|
await this.renderComponent();
|
||||||
|
assert.dom(DATE_RANGE.edit).doesNotExist('it does not render if timestamps are provided');
|
||||||
|
});
|
||||||
|
|
||||||
test('it formats modal inputs to ISO string timestamps', async function (assert) {
|
test('it formats modal inputs to ISO string timestamps', async function (assert) {
|
||||||
this.startTimestamp = undefined;
|
this.startTimestamp = undefined;
|
||||||
await this.renderComponent();
|
await this.renderComponent();
|
||||||
@ -80,7 +86,7 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it does not allow the current month to be selected as a start date or as an end date', async function (assert) {
|
test('it does not allow the current month to be selected as a start date or as an end date', async function (assert) {
|
||||||
this.owner.lookup('service:version').type = 'community';
|
this.version.type = 'community';
|
||||||
this.endTimestamp = undefined;
|
this.endTimestamp = undefined;
|
||||||
const currentMonth = format(timestamp.now(), 'yyyy-MM');
|
const currentMonth = format(timestamp.now(), 'yyyy-MM');
|
||||||
|
|
||||||
@ -91,7 +97,7 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||||||
|
|
||||||
assert.dom(DATE_RANGE.validation).hasText('You cannot select the current month or beyond.');
|
assert.dom(DATE_RANGE.validation).hasText('You cannot select the current month or beyond.');
|
||||||
await click(GENERAL.submitButton);
|
await click(GENERAL.submitButton);
|
||||||
assert.false(this.onChange.called);
|
assert.false(this.onChange.called, 'it does not call @onChange callback');
|
||||||
|
|
||||||
// This tests validation when the end date is the current month and start is valid.
|
// This tests validation when the end date is the current month and start is valid.
|
||||||
// If start is current month and end is a valid prior selection, it will run into the validation error of start being after end date
|
// If start is current month and end is a valid prior selection, it will run into the validation error of start being after end date
|
||||||
@ -99,7 +105,22 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||||||
await fillIn(DATE_RANGE.editDate('start'), '2018-01');
|
await fillIn(DATE_RANGE.editDate('start'), '2018-01');
|
||||||
await fillIn(DATE_RANGE.editDate('end'), currentMonth);
|
await fillIn(DATE_RANGE.editDate('end'), currentMonth);
|
||||||
await click(GENERAL.submitButton);
|
await click(GENERAL.submitButton);
|
||||||
assert.false(this.onChange.called);
|
assert.false(this.onChange.called, 'it does not call @onChange callback');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it allows the current month to be selected if enterprise and there is not a @billingStartTime', async function (assert) {
|
||||||
|
this.version.type = 'enterprise';
|
||||||
|
this.endTimestamp = undefined;
|
||||||
|
const currentMonth = format(timestamp.now(), 'yyyy-MM');
|
||||||
|
|
||||||
|
await this.renderComponent();
|
||||||
|
await click(DATE_RANGE.edit);
|
||||||
|
await fillIn(DATE_RANGE.editDate('start'), currentMonth);
|
||||||
|
await fillIn(DATE_RANGE.editDate('end'), currentMonth);
|
||||||
|
|
||||||
|
assert.dom(DATE_RANGE.validation).doesNotExist();
|
||||||
|
await click(GENERAL.submitButton);
|
||||||
|
assert.true(this.onChange.called, 'it calls @onChange callback');
|
||||||
});
|
});
|
||||||
|
|
||||||
module('enterprise', function (hooks) {
|
module('enterprise', function (hooks) {
|
||||||
@ -126,6 +147,19 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it renders date range modal if there are no timestamps provided', async function (assert) {
|
||||||
|
this.billingStartTime = '';
|
||||||
|
this.startTimestamp = '';
|
||||||
|
this.endTimestamp = '';
|
||||||
|
await this.renderComponent();
|
||||||
|
assert
|
||||||
|
.dom(DATE_RANGE.edit)
|
||||||
|
.exists('it renders button to open date range modal')
|
||||||
|
.hasText('Set date range');
|
||||||
|
await click(DATE_RANGE.edit);
|
||||||
|
assert.dom(DATE_RANGE.editModal).exists();
|
||||||
|
});
|
||||||
|
|
||||||
test('it updates toggle text when a new date is selected', async function (assert) {
|
test('it updates toggle text when a new date is selected', async function (assert) {
|
||||||
this.onChange = ({ start_time }) => this.set('startTimestamp', start_time);
|
this.onChange = ({ start_time }) => this.set('startTimestamp', start_time);
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
|||||||
this.downloadStub = Sinon.stub(this.owner.lookup('service:download'), 'download');
|
this.downloadStub = Sinon.stub(this.owner.lookup('service:download'), 'download');
|
||||||
this.startTimestamp = '2022-06-01T23:00:11.050Z';
|
this.startTimestamp = '2022-06-01T23:00:11.050Z';
|
||||||
this.endTimestamp = '2022-12-01T23:00:11.050Z';
|
this.endTimestamp = '2022-12-01T23:00:11.050Z';
|
||||||
|
this.billingStartTime = this.startTimestamp;
|
||||||
this.upgradesDuringActivity = [];
|
this.upgradesDuringActivity = [];
|
||||||
this.noData = undefined;
|
this.noData = undefined;
|
||||||
this.server.post('/sys/capabilities-self', () =>
|
this.server.post('/sys/capabilities-self', () =>
|
||||||
@ -34,7 +35,7 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
|||||||
this.renderComponent = async () => {
|
this.renderComponent = async () => {
|
||||||
return render(hbs`
|
return render(hbs`
|
||||||
<Clients::PageHeader
|
<Clients::PageHeader
|
||||||
@billingStartTime={{this.startTimestamp}}
|
@billingStartTime={{this.billingStartTime}}
|
||||||
@startTimestamp={{this.startTimestamp}}
|
@startTimestamp={{this.startTimestamp}}
|
||||||
@endTimestamp={{this.endTimestamp}}
|
@endTimestamp={{this.endTimestamp}}
|
||||||
@upgradesDuringActivity={{this.upgradesDuringActivity}}
|
@upgradesDuringActivity={{this.upgradesDuringActivity}}
|
||||||
@ -279,5 +280,13 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
|||||||
await this.renderComponent();
|
await this.renderComponent();
|
||||||
assert.dom(this.element).hasTextContaining('Client Usage For data period:');
|
assert.dom(this.element).hasTextContaining('Client Usage For data period:');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it allows date editing if no billing start time is provided', async function (assert) {
|
||||||
|
this.billingStartTime = '';
|
||||||
|
await this.renderComponent();
|
||||||
|
assert.dom(CLIENT_COUNT.dateRange.edit).exists('it renders edit button to open modal').hasText('Edit');
|
||||||
|
assert.dom(CLIENT_COUNT.dateRange.dateDisplay('start')).hasText('June 2022');
|
||||||
|
assert.dom(CLIENT_COUNT.dateRange.dateDisplay('end')).hasText('December 2022');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user