mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 18:48:08 +00:00 
			
		
		
		
	UI: remove current_billing_period from dashboard activity log request (#27559)
				
					
				
			* remove current_billing_period from dashboard request * add changelog * remove timestamp from assertion * update mirage
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/27559.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/27559.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| ```release-note:improvement | ||||
| ui: Remove deprecated `current_billing_period` from dashboard activity log request | ||||
| ``` | ||||
| @@ -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 | ||||
| @@ -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`; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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." | ||||
| @@ -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}} | ||||
| @@ -14,10 +14,10 @@ | ||||
|  | ||||
|   <hr class="has-background-gray-100" /> | ||||
|  | ||||
|   {{#if this.hasActivity}} | ||||
|   {{#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}} /> | ||||
| @@ -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> | ||||
|     {{/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"}} /> | ||||
|       <Clients::NoData @config={{this.activityConfig}} /> | ||||
|     {{/if}} | ||||
|   {{/if}} | ||||
| </Hds::Card::Container> | ||||
| @@ -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() { | ||||
| @@ -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; | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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(); | ||||
|     } | ||||
|   | ||||
| @@ -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])); | ||||
|   | ||||
							
								
								
									
										93
									
								
								ui/tests/integration/components/clients/no-data-test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								ui/tests/integration/components/clients/no-data-test.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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.' | ||||
|       ); | ||||
|   }); | ||||
| }); | ||||
| @@ -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'; | ||||
| @@ -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 />`); | ||||
| @@ -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]'); | ||||
| @@ -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: {} }, | ||||
|       }; | ||||
|     }); | ||||
| @@ -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.' | ||||
|       ); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 claire bontempo
					claire bontempo