Skip to content

Commit

Permalink
UI: remove current_billing_period from dashboard activity log reque…
Browse files Browse the repository at this point in the history
…st (#27559)

* remove current_billing_period from dashboard request

* add changelog

* remove timestamp from assertion

* update mirage
  • Loading branch information
hellobontempo committed Jun 21, 2024
1 parent 5f078e2 commit 6f00ce4
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 36 deletions.
3 changes: 3 additions & 0 deletions changelog/27559.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: Remove deprecated `current_billing_period` from dashboard activity log request
```
9 changes: 9 additions & 0 deletions ui/app/adapters/clients/activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import ApplicationAdapter from '../application';
import { formatDateObject } from 'core/utils/client-count-utils';
import { debug } from '@ember/debug';

export default class ActivityAdapter extends ApplicationAdapter {
// javascript localizes new Date() objects but all activity log data is stored in UTC
Expand Down Expand Up @@ -33,4 +34,12 @@ export default class ActivityAdapter extends ApplicationAdapter {
});
}
}

urlForFindRecord(id) {
// debug reminder so model is stored in Ember data with the same id for consistency
if (id !== 'clients/activity') {
debug(`findRecord('clients/activity') should pass 'clients/activity' as the id, you passed: '${id}'`);
}
return `${this.buildURL()}/internal/counters/activity`;
}
}
10 changes: 8 additions & 2 deletions ui/app/components/clients/no-data.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
SPDX-License-Identifier: BUSL-1.1
~}}

{{#if (eq @config.enabled "On")}}
{{#if (or @config.reportingEnabled (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."
class="is-shadowless"
/>
{{else}}
{{else if @config}}
<EmptyState
@title="Data tracking is disabled"
@message="Tracking is disabled, and no data is being collected. To turn it on, edit the configuration."
Expand All @@ -24,4 +24,10 @@
/>
{{/if}}
</EmptyState>
{{else}}
<EmptyState
@title="Activity configuration data is unavailable"
@message="Reporting status is unknown and could be enabled or disabled. Check the Vault logs for more information."
class="is-shadowless"
/>
{{/if}}
16 changes: 7 additions & 9 deletions ui/app/components/dashboard/client-count-card.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

<hr class="has-background-gray-100" />

{{#if this.hasActivity}}
{{#if this.fetchClientActivity.isRunning}}
<VaultLogoSpinner />
{{else}}
{{#if this.fetchClientActivity.isRunning}}
<VaultLogoSpinner />
{{else}}
{{#if this.activityData}}
<div class="is-grid grid-2-columns grid-gap-2 has-top-margin-m grid-align-items-start is-flex-v-centered">
<StatText @label="Total" @value={{this.activityData.total.clients}} @size="l" @subText={{this.statSubText.total}} />
<StatText @label="New" @value={{this.currentMonthActivityTotalCount}} @size="l" @subText={{this.statSubText.new}} />
Expand All @@ -34,15 +34,13 @@
{{on "click" (perform this.fetchClientActivity)}}
data-test-refresh
/>
<small class="has-left-margin-xs has-text-grey">
<small class="has-left-margin-xs has-text-grey" data-test-updated-timestamp>
Updated
{{date-format this.updatedAt "MMM d yyyy, h:mm:ss aaa" withTimeZone=true}}
</small>
</div>
{{else}}
<Clients::NoData @config={{this.activityConfig}} />
{{/if}}
{{else}}
{{! This will likely never show since the clients activity api has 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={{hash enabled="On"}} />
{{/if}}
</Hds::Card::Container>
9 changes: 4 additions & 5 deletions ui/app/components/dashboard/client-count-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class DashboardClientCountCard extends Component {
@service store;

@tracked activityData = null;
@tracked hasActivity = false;
@tracked activityConfig = null;
@tracked updatedAt = null;

constructor() {
Expand Down Expand Up @@ -53,11 +53,10 @@ export default class DashboardClientCountCard extends Component {
this.updatedAt = timestamp.now().toISOString();

try {
this.activityData = yield this.store.queryRecord('clients/activity', {
current_billing_period: true,
});
this.hasActivity = this.activityData.id === 'no-data' ? false : true;
this.activityData = yield this.store.findRecord('clients/activity', 'clients/activity');
} catch (error) {
// used for rendering the "No data" empty state, swallow any errors requesting config data
this.activityConfig = yield this.store.queryRecord('clients/config', {}).catch(() => null);
this.error = error;
}
}
Expand Down
2 changes: 2 additions & 0 deletions ui/app/models/clients/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ export default class ClientsConfigModel extends Model {

@attr('number') minimumRetentionMonths;

// refers specifically to the activitylog and will always be on for enterprise
@attr('string') enabled;

// reporting_enabled is for automated reporting and only true of the customer hasn’t opted-out of automated license reporting
@attr('boolean') reportingEnabled;

@attr('date') billingStartTimestamp;
Expand Down
6 changes: 3 additions & 3 deletions ui/mirage/handlers/clients.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,9 @@ export default function (server) {

server.get('/sys/internal/counters/activity', (schema, req) => {
let { start_time, end_time } = req.queryParams;
if (req.queryParams.current_billing_period) {
// { current_billing_period: true } automatically queries the activity log
// from the builtin license start timestamp to the current month
if (!start_time && !end_time) {
// if there are no date query params, the activity log default behavior
// queries from the builtin license start timestamp to the current month
start_time = LICENSE_START.toISOString();
end_time = STATIC_NOW.toISOString();
}
Expand Down
2 changes: 1 addition & 1 deletion ui/tests/acceptance/dashboard-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ module('Acceptance | landing page dashboard', function (hooks) {
assert.true(version.isEnterprise, 'version is enterprise');
assert.strictEqual(currentURL(), '/vault/dashboard');
assert.dom(DASHBOARD.cardName('client-count')).exists();
const response = await this.store.peekRecord('clients/activity', 'some-activity-id');
const response = await this.store.findRecord('clients/activity', 'clients/activity');
assert.dom('[data-test-client-count-title]').hasText('Client count');
assert.dom('[data-test-stat-text="Total"] .stat-label').hasText('Total');
assert.dom('[data-test-stat-text="Total"] .stat-value').hasText(formatNumber([response.total.clients]));
Expand Down
93 changes: 93 additions & 0 deletions ui/tests/integration/components/clients/no-data-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs';

module('Integration | Component | clients/no-data', function (hooks) {
setupRenderingTest(hooks);
setupMirage(hooks);

hooks.beforeEach(async function () {
this.server.post('/sys/capabilities-self', allowAllCapabilitiesStub());
this.store = this.owner.lookup('service:store');
this.setConfig = async (data) => {
// the clients/config model does some funky serializing for the "enabled" param
// so stubbing the request here instead of just the model for additional coverage
this.server.get('sys/internal/counters/config', () => {
return {
request_id: '25a94b99-b49a-c4ac-cb7b-5ba0eb390a25',
data,
};
});
return this.store.queryRecord('clients/config', {});
};
this.renderComponent = async () => {
return render(hbs`<Clients::NoData @config={{this.config}} />`);
};
});

test('it renders empty state when enabled is "on"', async function (assert) {
assert.expect(2);
const data = {
enabled: 'default-enabled',
reporting_enabled: false,
};
``;
this.config = await this.setConfig(data);
await this.renderComponent();
assert.dom(GENERAL.emptyStateTitle).hasText('No data received');
assert
.dom(GENERAL.emptyStateMessage)
.hasText('Tracking is turned on and Vault is gathering data. It should appear here within 30 minutes.');
});

test('it renders empty state when reporting_enabled is true', async function (assert) {
assert.expect(2);
const data = {
enabled: 'default-disabled',
reporting_enabled: true,
};
this.config = await this.setConfig(data);
await this.renderComponent();
assert.dom(GENERAL.emptyStateTitle).hasText('No data received');
assert
.dom(GENERAL.emptyStateMessage)
.hasText('Tracking is turned on and Vault is gathering data. It should appear here within 30 minutes.');
});

test('it renders empty state when reporting is fully disabled', async function (assert) {
assert.expect(2);
const data = {
enabled: 'default-disabled',
reporting_enabled: false,
};
this.config = await this.setConfig(data);
await this.renderComponent();
assert.dom(GENERAL.emptyStateTitle).hasText('Data tracking is disabled');
assert
.dom(GENERAL.emptyStateMessage)
.hasText(
'Tracking is disabled, and no data is being collected. To turn it on, edit the configuration.'
);
});

test('it renders empty state when config data is not available', async function (assert) {
assert.expect(2);
this.config = null;
await this.renderComponent();
assert.dom(GENERAL.emptyStateTitle).hasText('Activity configuration data is unavailable');
assert
.dom(GENERAL.emptyStateMessage)
.hasText(
'Reporting status is unknown and could be enabled or disabled. Check the Vault logs for more information.'
);
});
});
55 changes: 39 additions & 16 deletions ui/tests/integration/components/dashboard/client-count-card-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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 { Response } from 'miragejs';
import sinon from 'sinon';
import { STATIC_NOW } from 'vault/mirage/handlers/clients';
import timestamp from 'core/utils/timestamp';
Expand All @@ -21,21 +22,14 @@ module('Integration | Component | dashboard/client-count-card', function (hooks)
setupMirage(hooks);

test('it should display client count information', async function (assert) {
sinon.replace(timestamp, 'now', sinon.fake.returns(STATIC_NOW));
assert.expect(5);
assert.expect(6);
sinon.replace(timestamp, 'now', sinon.fake.returns(STATIC_NOW)); // 1/25/24
const { months, total } = ACTIVITY_RESPONSE_STUB;
const [latestMonth] = months.slice(-1);
this.server.get('sys/internal/counters/activity', (schema, req) => {
this.server.get('sys/internal/counters/activity', () => {
// this assertion should be hit twice, once initially and then again clicking 'refresh'
assert.propEqual(
req.queryParams,
{ current_billing_period: 'true' },
'it makes request to sys/internal/counters/activity with builtin license start time'
);
return {
request_id: 'some-activity-id',
data: ACTIVITY_RESPONSE_STUB,
};
assert.true(true, 'makes request to sys/internal/counters/activity');
return { data: ACTIVITY_RESPONSE_STUB };
});

await render(hbs`<Dashboard::ClientCountCard />`);
Expand All @@ -54,6 +48,7 @@ module('Integration | Component | dashboard/client-count-card', function (hooks)
latestMonth.new_clients.counts.clients,
])}`
);
assert.dom('[data-test-updated-timestamp]').hasTextContaining('Updated Jan 25 2024');

// fires second request to /activity
await click('[data-test-refresh]');
Expand All @@ -65,7 +60,6 @@ module('Integration | Component | dashboard/client-count-card', function (hooks)
// stubbing this unrealistic response just to test component subtext logic
this.server.get('sys/internal/counters/activity', () => {
return {
request_id: 'some-activity-id',
data: { by_namespace: [], months: [], total: {} },
};
});
Expand All @@ -75,19 +69,48 @@ module('Integration | Component | dashboard/client-count-card', function (hooks)
assert.dom(CLIENT_COUNT.statText('New')).hasText('New No new client data available. -');
});

test('it shows empty state if no activity data', async function (assert) {
test('it shows empty state if no activity data and reporting is enabled', async function (assert) {
// the activity response has changed and now should ALWAYS return something
// but adding this test until we update the adapter to reflect that
assert.expect(3);
assert.expect(4);
this.server.get('sys/internal/counters/activity', () => {
assert.true(true, 'makes request to sys/internal/counters/activity');
return { data: {} };
});

this.server.get('sys/internal/counters/config', () => {
assert.true(true, 'makes request to sys/internal/counters/config');
return {
request_id: '25a94b99-b49a-c4ac-cb7b-5ba0eb390a25',
data: { reporting_enabled: true },
};
});
await render(hbs`<Dashboard::ClientCountCard />`);
assert.dom(GENERAL.emptyStateTitle).hasText('No data received');
assert
.dom(GENERAL.emptyStateMessage)
.hasText('Tracking is turned on and Vault is gathering data. It should appear here within 30 minutes.');
});

test('it shows empty state if no activity data and config data is unavailable', async function (assert) {
assert.expect(4);
this.server.get('sys/internal/counters/activity', () => {
assert.true(true, 'makes request to sys/internal/counters/activity');
return { data: {} };
});
this.server.get('sys/internal/counters/config', () => {
assert.true(true, 'makes request to sys/internal/counters/config');
return new Response(
403,
{ 'Content-Type': 'application/json' },
JSON.stringify({ errors: ['permission denied'] })
);
});
await render(hbs`<Dashboard::ClientCountCard />`);
assert.dom(GENERAL.emptyStateTitle).hasText('Activity configuration data is unavailable');
assert
.dom(GENERAL.emptyStateMessage)
.hasText(
'Reporting status is unknown and could be enabled or disabled. Check the Vault logs for more information.'
);
});
});

0 comments on commit 6f00ce4

Please sign in to comment.