mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-25 12:41:10 +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 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">
|
||||
{{if this.flags.isHvdManaged "Change data period" "Change billing period"}}
|
||||
</Hds::Text::Display>
|
||||
@ -34,17 +35,15 @@
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
</Hds::Dropdown>
|
||||
{{else}}
|
||||
{{! Hide if the user has made an initial query because dates can be updated by clicking "Edit" beside the date range }}
|
||||
{{#unless (and @startTimestamp @endTimestamp)}}
|
||||
<Hds::Button
|
||||
class="has-left-margin-xs"
|
||||
@text="Set date range"
|
||||
@icon="edit"
|
||||
{{on "click" (fn @setEditModalVisible true)}}
|
||||
data-test-date-range-edit
|
||||
/>
|
||||
{{/unless}}
|
||||
{{else if (not (and @startTimestamp @endTimestamp))}}
|
||||
{{! Hide if the user has already made an initial query because dates can be updated by clicking "Edit" beside the date range }}
|
||||
<Hds::Button
|
||||
class="has-left-margin-xs"
|
||||
@text="Set date range"
|
||||
@icon="edit"
|
||||
{{on "click" (fn @setEditModalVisible true)}}
|
||||
data-test-date-range-edit
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
@ -54,62 +53,61 @@
|
||||
Edit date range
|
||||
</M.Header>
|
||||
<M.Body>
|
||||
<Hds::Text::Body @tag="div">
|
||||
{{#if this.version.isCommunity}}
|
||||
<p class="has-bottom-margin-s">
|
||||
Use custom date ranges to query historic client count data. Query results do not include the current month.
|
||||
</p>
|
||||
<ul class="has-bottom-margin-s">
|
||||
<li>
|
||||
<strong>Start</strong>
|
||||
sets the month and year (inclusive) for the first month of client counting.
|
||||
</li>
|
||||
<li>
|
||||
<strong>End</strong>
|
||||
sets the month and year (inclusive) for the final month of client counting.
|
||||
</li>
|
||||
</ul>
|
||||
<p class="has-bottom-margin-s">
|
||||
We recommend setting
|
||||
{{/if}}
|
||||
<ul class="has-bottom-margin-s">
|
||||
<li>
|
||||
<strong>Start</strong>
|
||||
to your Vault deploy date to get the most accurate new and total client count estimations.</p>
|
||||
|
||||
<div class="clients-date-range-display">
|
||||
<div>
|
||||
<Hds::Form::TextInput::Field
|
||||
@type="month"
|
||||
@value={{this.modalStart}}
|
||||
max={{this.previousMonth}}
|
||||
id="start-month"
|
||||
name="modalStart"
|
||||
{{on "change" this.updateDate}}
|
||||
data-test-date-edit="start"
|
||||
as |F|
|
||||
>
|
||||
<F.Label>Start</F.Label>
|
||||
</Hds::Form::TextInput::Field>
|
||||
</div>
|
||||
<div>
|
||||
<Hds::Form::TextInput::Field
|
||||
@type="month"
|
||||
@value={{this.modalEnd}}
|
||||
max={{this.previousMonth}}
|
||||
id="end-month"
|
||||
name="modalEnd"
|
||||
{{on "change" this.updateDate}}
|
||||
data-test-date-edit="end"
|
||||
as |F|
|
||||
>
|
||||
<F.Label>End</F.Label>
|
||||
</Hds::Form::TextInput::Field>
|
||||
</div>
|
||||
sets the month and year (inclusive) for the first month of client counting.
|
||||
</li>
|
||||
<li>
|
||||
<strong>End</strong>
|
||||
sets the month and year (inclusive) for the final month of client counting.
|
||||
</li>
|
||||
</ul>
|
||||
<p class="has-bottom-margin-s">
|
||||
We recommend setting
|
||||
<strong>Start</strong>
|
||||
to your Vault deploy date to get the most accurate client counts.</p>
|
||||
<div class="clients-date-range-display">
|
||||
<div>
|
||||
<Hds::Form::TextInput::Field
|
||||
@type="month"
|
||||
@value={{this.modalStart}}
|
||||
max={{if this.version.isCommunity this.previousMonth (date-format this.currentMonth "yyyy-MM")}}
|
||||
id="start-month"
|
||||
name="modalStart"
|
||||
{{on "change" this.updateDate}}
|
||||
data-test-date-edit="start"
|
||||
as |F|
|
||||
>
|
||||
<F.Label>Start</F.Label>
|
||||
</Hds::Form::TextInput::Field>
|
||||
</div>
|
||||
{{#if this.validationError}}
|
||||
<Hds::Form::Error
|
||||
class="has-top-margin-xs"
|
||||
data-test-date-range-validation
|
||||
>{{this.validationError}}</Hds::Form::Error>
|
||||
{{/if}}
|
||||
</Hds::Text::Body>
|
||||
<div>
|
||||
<Hds::Form::TextInput::Field
|
||||
@type="month"
|
||||
@value={{this.modalEnd}}
|
||||
max={{if this.version.isCommunity this.previousMonth (date-format this.currentMonth "yyyy-MM")}}
|
||||
id="end-month"
|
||||
name="modalEnd"
|
||||
{{on "change" this.updateDate}}
|
||||
data-test-date-edit="end"
|
||||
as |F|
|
||||
>
|
||||
<F.Label>End</F.Label>
|
||||
</Hds::Form::TextInput::Field>
|
||||
</div>
|
||||
</div>
|
||||
{{#if this.validationError}}
|
||||
<Hds::Form::Error
|
||||
class="has-top-margin-xs"
|
||||
data-test-date-range-validation
|
||||
>{{this.validationError}}</Hds::Form::Error>
|
||||
{{/if}}
|
||||
</M.Body>
|
||||
<M.Footer as |F|>
|
||||
<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) {
|
||||
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 null;
|
||||
|
||||
@ -23,22 +23,21 @@
|
||||
/>
|
||||
</PH.Subtitle>
|
||||
{{/if}}
|
||||
{{#if this.version.isEnterprise}}
|
||||
<PH.Description class="has-text-weight-semibold flex">
|
||||
<p>
|
||||
{{if this.flags.isHvdManaged "For data period:" "For billing period:"}}
|
||||
<PH.Description>
|
||||
{{#if (and this.version.isEnterprise @billingStartTime)}}
|
||||
{{! 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: "}}
|
||||
<span data-test-date-range="start">{{this.formattedStartDate}}</span>
|
||||
-
|
||||
<span data-test-date-range="end">{{this.formattedEndDate}}</span>
|
||||
</p>
|
||||
</PH.Description>
|
||||
{{/if}}
|
||||
<PH.Description>
|
||||
{{#if this.showCommunity}}
|
||||
{{else if (and @startTimestamp @endTimestamp)}}
|
||||
{{! If these timestamps exist an initial query has already been made using the modal in Clients::DateRange }}
|
||||
<div class="has-top-padding-m">
|
||||
<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
|
||||
choose a different date range.</Hds::Text::Body>
|
||||
<Hds::Text::Body @tag="p" @size="100" color="faint">Click 'Edit' to select a different date range.</Hds::Text::Body>
|
||||
<Hds::Text::Body @tag="p">
|
||||
<span data-test-date-range="start">{{this.formattedStartDate}}</span>
|
||||
-
|
||||
|
||||
@ -95,10 +95,6 @@ export default class ClientsPageHeaderComponent extends Component {
|
||||
return `clients_export${ns}${csvDateRange}`;
|
||||
}
|
||||
|
||||
get showCommunity() {
|
||||
return this.version.isCommunity && !!this.formattedStartDate && !!this.formattedEndDate;
|
||||
}
|
||||
|
||||
async getExportData() {
|
||||
const adapter = this.store.adapterFor('clients/activity');
|
||||
const { startTimestamp, endTimestamp } = this.args;
|
||||
|
||||
@ -39,7 +39,10 @@ export default class ClientsCountsPageComponent extends Component<Args> {
|
||||
}
|
||||
|
||||
get formattedBillingStartDate() {
|
||||
return this.args.config.billingStartTimestamp.toISOString();
|
||||
if (this.args.config?.billingStartTimestamp) {
|
||||
return this.args.config.billingStartTimestamp.toISOString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// passed into page-header for the export modal alert
|
||||
|
||||
@ -28,7 +28,7 @@ export default class ClientsOverviewPageComponent extends Component<Args> {
|
||||
get byMonthClients() {
|
||||
// HVD clusters are billed differently and the monthly total is the important metric.
|
||||
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.
|
||||
// (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 )
|
||||
@cached
|
||||
get activityData() {
|
||||
// If no month is selected the table displays all of the activity for the queried date range.
|
||||
const selectedMonth = this.args.filterQueryParams.month;
|
||||
@ -49,9 +50,12 @@ export default class ClientsOverviewPageComponent extends Component<Args> {
|
||||
|
||||
@cached
|
||||
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() {
|
||||
if (this.activityData?.length) {
|
||||
// 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);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.version = this.owner.lookup('service:version');
|
||||
Sinon.replace(timestamp, 'now', Sinon.fake.returns(new Date('2018-04-03T14:15:30')));
|
||||
this.now = timestamp.now();
|
||||
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) {
|
||||
this.startTimestamp = undefined;
|
||||
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) {
|
||||
this.owner.lookup('service:version').type = 'community';
|
||||
this.version.type = 'community';
|
||||
this.endTimestamp = undefined;
|
||||
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.');
|
||||
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.
|
||||
// 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('end'), currentMonth);
|
||||
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) {
|
||||
@ -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) {
|
||||
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.startTimestamp = '2022-06-01T23:00:11.050Z';
|
||||
this.endTimestamp = '2022-12-01T23:00:11.050Z';
|
||||
this.billingStartTime = this.startTimestamp;
|
||||
this.upgradesDuringActivity = [];
|
||||
this.noData = undefined;
|
||||
this.server.post('/sys/capabilities-self', () =>
|
||||
@ -34,7 +35,7 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
||||
this.renderComponent = async () => {
|
||||
return render(hbs`
|
||||
<Clients::PageHeader
|
||||
@billingStartTime={{this.startTimestamp}}
|
||||
@billingStartTime={{this.billingStartTime}}
|
||||
@startTimestamp={{this.startTimestamp}}
|
||||
@endTimestamp={{this.endTimestamp}}
|
||||
@upgradesDuringActivity={{this.upgradesDuringActivity}}
|
||||
@ -279,5 +280,13 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
||||
await this.renderComponent();
|
||||
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