Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Update dashboard client count query to use billing_start_timestamp #26729

Merged

Conversation

hellobontempo
Copy link
Contributor

When we implemented Census reporting #20125 we stopped querying the /sys/license/status endpoint to instead retrieve the billing_start_timestamp from sys/internal/counters/config

This PR updates the UI dashboard to follow the same pattern so there is parity in the start_time used to make the request to the activity log. When the license query was removed in the client count refactor, we also stopped using local storage to save inputted dates so vault:ui-inputted-start-date is irrelevant now.

@hellobontempo hellobontempo requested a review from a team as a code owner May 1, 2024 10:05
@github-actions github-actions bot added the hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed label May 1, 2024
@hellobontempo hellobontempo added pr/no-changelog and removed hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed labels May 1, 2024
@hellobontempo hellobontempo added this to the 1.17.0-rc milestone May 1, 2024
@github-actions github-actions bot added the hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed label May 1, 2024
Copy link

github-actions bot commented May 1, 2024

Build Results:
All builds succeeded! ✅

Copy link

github-actions bot commented May 1, 2024

CI Results:
All Go tests succeeded! ✅

if (!this.clientConfig) {
// set config and license start time when component initializes
this.clientConfig = yield this.store.queryRecord('clients/config', {}).catch(() => {});
this.licenseStartTime = setStartTimeQuery(this.args.isEnterprise, this.clientConfig);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically I could just pass true here instead of this.args.isEnterprise because this component is wrapped in a conditional and only renders for enterprise versions. however, in case a future refactor shuffled conditionals, I wanted to preserve the logic in setStartTimeQuery which prevents from accidentally setting the billing start time to 0001-01-01T00:00:00Z for community versions

}

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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we no longer saved the inputted user start date in local storage

if (this.version.isEnterprise && this._hasConfig(config)) {
startTime = getUnixTime(config.billingStartTimestamp);
}
const startTime = setStartTimeQuery(this.version.isEnterprise, config);
Copy link
Contributor Author

@hellobontempo hellobontempo May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this logic is fairly simple and only used in two places, I wanted to consolidate it so that setting the start time was handled the same in both places the /activity log is queried. See Jira VAULT-25315 for more context around the bug introduced by not checking the version.

@@ -55,7 +55,7 @@
/>
<small class="has-left-margin-xs has-text-grey">
Updated
{{date-format this.updatedAt "MMM dd, yyyy hh:mm:ss"}}
{{date-format this.updatedAt "MMM d yyyy, h:mm:ss aaa" withTimeZone=true}}
Copy link
Contributor Author

@hellobontempo hellobontempo May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This format is consistent with the format we show response timestamps elsewhere in the app
Screenshot 2024-05-01 at 1 52 17 PM

Copy link
Contributor

@andaley andaley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice! i think moving this into a util to ensure the same start time is applied in both areas is a great solution. only request i have here is tests -- could we get some coverage for setStartTimeQuery in the client-count-utils-test? it'd be good to test for community, ent, an empty config, a populated config etc.

other than that looks great 🙌

* ```
* @param {object} license - license object passed from the parent
*
* <Dashboard::ClientCountCard />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* <Dashboard::ClientCountCard />
* <Dashboard::ClientCountCard @isEnterprise={{@version.isEnterprise}} />

looks like we're missing that arg 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah! I added that last minute - thanks!

@@ -140,43 +149,11 @@ module('Integration | Component | dashboard/overview', function (hooks) {
assert.dom(DASHBOARD.cardName('client-count')).exists();
});

test('it should hide client count on enterprise w/o license ', async function (assert) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test didn't really make sense because enterprise versions don't exist without a license

assert.expect(4);
this.server.get('sys/internal/counters/activity', () => {
// this assertion should NOT be hit in this test
assert.true(true, 'uh oh! makes request to sys/internal/counters/activity');
Copy link
Contributor

@andaley andaley May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert.true(true, 'uh oh! makes request to sys/internal/counters/activity');
throw new Error('uh oh! makes request to sys/internal/counters/activity');

^ another way to more clearly verify that the endpoint isn't called in this test. throwing an error will cause the test to fail. if you take this approach, you'll also want to adjust the assert.expect(4) to 3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea! Much clearer 💡

Comment on lines +380 to +399
assert.strictEqual(setStartTimeQuery(true, {}), null, `it returns null if no permission to ${apiPath}`);
assert.strictEqual(
setStartTimeQuery(false, {}),
null,
`it returns null for community edition and no permission to ${apiPath}`
);
assert.strictEqual(
setStartTimeQuery(true, { billingStartTimestamp: new Date('2022-06-08T00:00:00Z') }),
1654646400,
'it returns unix time if enterprise and billing_start_timestamp exists'
);
assert.strictEqual(
setStartTimeQuery(false, { billingStartTimestamp: new Date('0001-01-01T00:00:00Z') }),
null,
'it returns null time for community edition even if billing_start_timestamp exists'
);
assert.strictEqual(
setStartTimeQuery(false, { foo: 'bar' }),
null,
'it returns null if billing_start_timestamp key does not exist'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ these assertions are normally things i'd split into separate tests within a module. for example:

module('#setStartTimeQuery', function (hooks) {
    test('community: it returns null with no permission to apiPath', async function (assert) {
      ...
    });

    test('community: it returns null when billing_start_time_exists', async function (assert) {
      ...
    });

    test('enterprise: it returns null when billing_start_time_exists', async function (assert) {
      ...
    }
  });

the main benefits of splitting them up this way are that it keeps the tests more isolated (and thus less flaky since there's no chance of the state of one assertion affecting the next one). it also makes them easier to read and debug (since you know exactly which situation failed).

not a blocker, since i know there's plenty of changes in this PR already but it's a pattern i'd encourage us to try out more 🙌

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great idea! I'll keep this as is because it's how all of the tests in this file are formatted right now. I think I'd rather update all of them to be separate modules at once. 🤔

Great feedback, though. I'll keep this in mind when writing new tests and add a todo comment to the top of this file to keep it in mind as a future improvement.

@hellobontempo hellobontempo enabled auto-merge (squash) May 2, 2024 14:46
@hellobontempo hellobontempo merged commit 6d0e4f6 into main May 2, 2024
31 checks passed
@hellobontempo hellobontempo deleted the ui/VAULT-25388/update-dashboard-client-count-query branch May 2, 2024 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hashicorp-contributed-pr If the PR is HashiCorp (i.e. not-community) contributed ui
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants