mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-31 02:28:09 +00:00 
			
		
		
		
	UI: fix undefined csv filename (#26485)
* fix undefined file name start date * add test coverage! * small copy changes * one last test! * add changelog;
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/26485.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/26485.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:bug | ||||||
|  | ui: fixes undefined start time in filename for downloaded client count attribution csv | ||||||
|  | ``` | ||||||
| @@ -99,13 +99,17 @@ | |||||||
|       </p> |       </p> | ||||||
|       <p class="has-bottom-margin-s is-subtitle-gray">SELECTED DATE {{if this.formattedEndDate " RANGE"}}</p> |       <p class="has-bottom-margin-s is-subtitle-gray">SELECTED DATE {{if this.formattedEndDate " RANGE"}}</p> | ||||||
|       <p class="has-bottom-margin-s" data-test-export-date-range> |       <p class="has-bottom-margin-s" data-test-export-date-range> | ||||||
|         {{this.parseAPITimestamp @startTimestamp "MMMM yyyy"}} |         {{this.formattedStartDate}} | ||||||
|         {{if this.formattedEndDate "-"}} |         {{if this.formattedEndDate "-"}} | ||||||
|         {{this.formattedEndDate}}</p> |         {{this.formattedEndDate}}</p> | ||||||
|     </M.Body> |     </M.Body> | ||||||
|     <M.Footer as |F|> |     <M.Footer as |F|> | ||||||
|       <Hds::ButtonSet> |       <Hds::ButtonSet> | ||||||
|         <Hds::Button @text="Export" {{on "click" (fn this.exportChartData this.formattedCsvFileName)}} /> |         <Hds::Button | ||||||
|  |           @text="Export" | ||||||
|  |           {{on "click" (fn this.exportChartData this.formattedCsvFileName)}} | ||||||
|  |           data-test-confirm-button | ||||||
|  |         /> | ||||||
|         <Hds::Button @text="Cancel" @color="secondary" {{on "click" F.close}} /> |         <Hds::Button @text="Cancel" @color="secondary" {{on "click" F.close}} /> | ||||||
|       </Hds::ButtonSet> |       </Hds::ButtonSet> | ||||||
|       {{#if @upgradesDuringActivity}} |       {{#if @upgradesDuringActivity}} | ||||||
|   | |||||||
| @@ -43,11 +43,8 @@ import { format, isSameMonth } from 'date-fns'; | |||||||
|  |  | ||||||
| export default class Attribution extends Component { | export default class Attribution extends Component { | ||||||
|   @service download; |   @service download; | ||||||
|  |  | ||||||
|   @tracked showCSVDownloadModal = false; |   @tracked showCSVDownloadModal = false; | ||||||
|  |  | ||||||
|   parseAPITimestamp = (time, format) => parseAPITimestamp(time, format); |  | ||||||
|  |  | ||||||
|   get attributionLegend() { |   get attributionLegend() { | ||||||
|     const attributionLegend = [ |     const attributionLegend = [ | ||||||
|       { key: 'entity_clients', label: 'entity clients' }, |       { key: 'entity_clients', label: 'entity clients' }, | ||||||
| @@ -60,6 +57,11 @@ export default class Attribution extends Component { | |||||||
|     return attributionLegend; |     return attributionLegend; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   get formattedStartDate() { | ||||||
|  |     if (!this.args.startTimestamp) return null; | ||||||
|  |     return parseAPITimestamp(this.args.startTimestamp, 'MMMM yyyy'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   get formattedEndDate() { |   get formattedEndDate() { | ||||||
|     if (!this.args.startTimestamp && !this.args.endTimestamp) return null; |     if (!this.args.startTimestamp && !this.args.endTimestamp) return null; | ||||||
|     // displays on CSV export modal, no need to display duplicate months and years |     // displays on CSV export modal, no need to display duplicate months and years | ||||||
| @@ -73,9 +75,6 @@ export default class Attribution extends Component { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   get isSingleNamespace() { |   get isSingleNamespace() { | ||||||
|     if (!this.args.totalClientAttribution) { |  | ||||||
|       return 'no data'; |  | ||||||
|     } |  | ||||||
|     // if a namespace is selected, then we're viewing top 10 auth methods (mounts) |     // if a namespace is selected, then we're viewing top 10 auth methods (mounts) | ||||||
|     return !!this.args.selectedNamespace; |     return !!this.args.selectedNamespace; | ||||||
|   } |   } | ||||||
| @@ -100,6 +99,9 @@ export default class Attribution extends Component { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   get chartText() { |   get chartText() { | ||||||
|  |     if (!this.args.totalClientAttribution) { | ||||||
|  |       return { description: 'There is a problem gathering data' }; | ||||||
|  |     } | ||||||
|     const dateText = this.formattedEndDate ? 'date range' : 'month'; |     const dateText = this.formattedEndDate ? 'date range' : 'month'; | ||||||
|     switch (this.isSingleNamespace) { |     switch (this.isSingleNamespace) { | ||||||
|       case true: |       case true: | ||||||
| @@ -121,10 +123,6 @@ export default class Attribution extends Component { | |||||||
|           }`, |           }`, | ||||||
|           totalCopy: `The total clients in the namespace for this ${dateText}. This number is useful for identifying overall usage volume.`, |           totalCopy: `The total clients in the namespace for this ${dateText}. This number is useful for identifying overall usage volume.`, | ||||||
|         }; |         }; | ||||||
|       case 'no data': |  | ||||||
|         return { |  | ||||||
|           description: 'There is a problem gathering data', |  | ||||||
|         }; |  | ||||||
|       default: |       default: | ||||||
|         return ''; |         return ''; | ||||||
|     } |     } | ||||||
| @@ -157,7 +155,7 @@ export default class Attribution extends Component { | |||||||
|     const csvData = []; |     const csvData = []; | ||||||
|     // added to clarify that the row of namespace totals without an auth method (blank) are not additional clients |     // added to clarify that the row of namespace totals without an auth method (blank) are not additional clients | ||||||
|     // but indicate the total clients for that ns, including its auth methods |     // but indicate the total clients for that ns, including its auth methods | ||||||
|     const upgrade = this.args.upgradesDuringActivity.length |     const upgrade = this.args.upgradesDuringActivity?.length | ||||||
|       ? `\n **data contains an upgrade, mount summation may not equal namespace totals` |       ? `\n **data contains an upgrade, mount summation may not equal namespace totals` | ||||||
|       : ''; |       : ''; | ||||||
|     const descriptionOfBlanks = this.isSingleNamespace |     const descriptionOfBlanks = this.isSingleNamespace | ||||||
| @@ -165,7 +163,7 @@ export default class Attribution extends Component { | |||||||
|       : `\n  *namespace totals, inclusive of mount clients${upgrade}`; |       : `\n  *namespace totals, inclusive of mount clients${upgrade}`; | ||||||
|     const csvHeader = [ |     const csvHeader = [ | ||||||
|       'Namespace path', |       'Namespace path', | ||||||
|       `"Mount path ${descriptionOfBlanks}"`, |       `Mount path${descriptionOfBlanks}`, | ||||||
|       'Total clients', |       'Total clients', | ||||||
|       'Entity clients', |       'Entity clients', | ||||||
|       'Non-entity clients', |       'Non-entity clients', | ||||||
| @@ -216,10 +214,10 @@ export default class Attribution extends Component { | |||||||
|  |  | ||||||
|   get formattedCsvFileName() { |   get formattedCsvFileName() { | ||||||
|     const endRange = this.formattedEndDate ? `-${this.formattedEndDate}` : ''; |     const endRange = this.formattedEndDate ? `-${this.formattedEndDate}` : ''; | ||||||
|     const csvDateRange = this.formattedStartDate + endRange; |     const csvDateRange = this.formattedStartDate ? `_${this.formattedStartDate + endRange}` : ''; | ||||||
|     return this.isSingleNamespace |     return this.isSingleNamespace | ||||||
|       ? `clients_by_mount_path_${csvDateRange}` |       ? `clients_by_mount_path${csvDateRange}` | ||||||
|       : `clients_by_namespace_${csvDateRange}`; |       : `clients_by_namespace${csvDateRange}`; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   get modalExportText() { |   get modalExportText() { | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import { endOfMonth, formatRFC3339 } from 'date-fns'; | |||||||
| import { click } from '@ember/test-helpers'; | import { click } from '@ember/test-helpers'; | ||||||
| import subMonths from 'date-fns/subMonths'; | import subMonths from 'date-fns/subMonths'; | ||||||
| import timestamp from 'core/utils/timestamp'; | import timestamp from 'core/utils/timestamp'; | ||||||
|  | import { GENERAL } from 'vault/tests/helpers/general-selectors'; | ||||||
|  |  | ||||||
| module('Integration | Component | clients/attribution', function (hooks) { | module('Integration | Component | clients/attribution', function (hooks) { | ||||||
|   setupRenderingTest(hooks); |   setupRenderingTest(hooks); | ||||||
| @@ -19,7 +20,9 @@ module('Integration | Component | clients/attribution', function (hooks) { | |||||||
|   hooks.before(function () { |   hooks.before(function () { | ||||||
|     sinon.stub(timestamp, 'now').callsFake(() => new Date('2018-04-03T14:15:30')); |     sinon.stub(timestamp, 'now').callsFake(() => new Date('2018-04-03T14:15:30')); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   hooks.beforeEach(function () { |   hooks.beforeEach(function () { | ||||||
|  |     this.csvDownloadStub = sinon.stub(this.owner.lookup('service:download'), 'csv'); | ||||||
|     const mockNow = timestamp.now(); |     const mockNow = timestamp.now(); | ||||||
|     this.mockNow = mockNow; |     this.mockNow = mockNow; | ||||||
|     this.set('startTimestamp', formatRFC3339(subMonths(mockNow, 6))); |     this.set('startTimestamp', formatRFC3339(subMonths(mockNow, 6))); | ||||||
| @@ -40,8 +43,10 @@ module('Integration | Component | clients/attribution', function (hooks) { | |||||||
|       { label: 'auth2/', clients: 2, entity_clients: 1, non_entity_clients: 1 }, |       { label: 'auth2/', clients: 2, entity_clients: 1, non_entity_clients: 1 }, | ||||||
|     ]); |     ]); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   hooks.after(function () { |   hooks.after(function () { | ||||||
|     timestamp.now.restore(); |     timestamp.now.restore(); | ||||||
|  |     this.csvDownloadStub.restore(); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   test('it renders empty state with no data', async function (assert) { |   test('it renders empty state with no data', async function (assert) { | ||||||
| @@ -227,4 +232,92 @@ module('Integration | Component | clients/attribution', function (hooks) { | |||||||
|       .hasText('Export attribution data', 'modal appears to export csv'); |       .hasText('Export attribution data', 'modal appears to export csv'); | ||||||
|     assert.dom('[ data-test-export-date-range]').includesText('June 2022 - December 2022'); |     assert.dom('[ data-test-export-date-range]').includesText('June 2022 - December 2022'); | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|  |   test('it downloads csv data for date range', async function (assert) { | ||||||
|  |     assert.expect(2); | ||||||
|  |  | ||||||
|  |     await render(hbs` | ||||||
|  |       <Clients::Attribution | ||||||
|  |         @totalClientAttribution={{this.totalClientAttribution}} | ||||||
|  |         @responseTimestamp={{this.timestamp}} | ||||||
|  |         @startTimestamp="2022-06-01T23:00:11.050Z" | ||||||
|  |         @endTimestamp="2022-12-01T23:00:11.050Z" | ||||||
|  |         /> | ||||||
|  |     `); | ||||||
|  |     await click('[data-test-attribution-export-button]'); | ||||||
|  |     await click(GENERAL.confirmButton); | ||||||
|  |     const [filename, content] = this.csvDownloadStub.lastCall.args; | ||||||
|  |     assert.strictEqual(filename, 'clients_by_namespace_June 2022-December 2022', 'csv has expected filename'); | ||||||
|  |     assert.strictEqual( | ||||||
|  |       content, | ||||||
|  |       `Namespace path,Mount path\n  *namespace totals, inclusive of mount clients,Total clients,Entity clients,Non-entity clients\nsecond,*,10,7,3\nfirst,*,5,3,2`, | ||||||
|  |       'csv has expected content' | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it downloads csv data for a single month', async function (assert) { | ||||||
|  |     assert.expect(2); | ||||||
|  |     await render(hbs` | ||||||
|  |       <Clients::Attribution | ||||||
|  |         @totalClientAttribution={{this.totalClientAttribution}} | ||||||
|  |         @responseTimestamp={{this.timestamp}} | ||||||
|  |         @startTimestamp="2022-06-01T23:00:11.050Z" | ||||||
|  |         @endTimestamp="2022-06-21T23:00:11.050Z" | ||||||
|  |         /> | ||||||
|  |     `); | ||||||
|  |     await click('[data-test-attribution-export-button]'); | ||||||
|  |     await click(GENERAL.confirmButton); | ||||||
|  |     const [filename, content] = this.csvDownloadStub.lastCall.args; | ||||||
|  |     assert.strictEqual(filename, 'clients_by_namespace_June 2022', 'csv has single month in filename'); | ||||||
|  |     assert.strictEqual( | ||||||
|  |       content, | ||||||
|  |       `Namespace path,Mount path\n  *namespace totals, inclusive of mount clients,Total clients,Entity clients,Non-entity clients\nsecond,*,10,7,3\nfirst,*,5,3,2`, | ||||||
|  |       'csv has expected content' | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('it downloads csv data when a namespace is selected', async function (assert) { | ||||||
|  |     assert.expect(2); | ||||||
|  |     this.selectedNamespace = 'second'; | ||||||
|  |  | ||||||
|  |     await render(hbs` | ||||||
|  |       <Clients::Attribution | ||||||
|  |         @totalClientAttribution={{this.namespaceMountsData}} | ||||||
|  |         @selectedNamespace={{this.selectedNamespace}} | ||||||
|  |         @responseTimestamp={{this.timestamp}} | ||||||
|  |         @startTimestamp="2022-06-01T23:00:11.050Z" | ||||||
|  |         @endTimestamp="2022-12-21T23:00:11.050Z" | ||||||
|  |         /> | ||||||
|  |     `); | ||||||
|  |  | ||||||
|  |     await click('[data-test-attribution-export-button]'); | ||||||
|  |     await click(GENERAL.confirmButton); | ||||||
|  |     const [filename, content] = this.csvDownloadStub.lastCall.args; | ||||||
|  |     assert.strictEqual( | ||||||
|  |       filename, | ||||||
|  |       'clients_by_mount_path_June 2022-December 2022', | ||||||
|  |       'csv has expected filename for a selected namespace' | ||||||
|  |     ); | ||||||
|  |     assert.strictEqual( | ||||||
|  |       content, | ||||||
|  |       `Namespace path,Mount path,Total clients,Entity clients,Non-entity clients\nsecond,auth1/,3,2,1\nsecond,auth2/,2,1,1`, | ||||||
|  |       'csv has expected content for a selected namespace' | ||||||
|  |     ); | ||||||
|  |   }); | ||||||
|  |  | ||||||
|  |   test('csv filename omits date if no start/end timestamp', async function (assert) { | ||||||
|  |     assert.expect(1); | ||||||
|  |  | ||||||
|  |     await render(hbs` | ||||||
|  |       <Clients::Attribution | ||||||
|  |         @totalClientAttribution={{this.totalClientAttribution}} | ||||||
|  |         @responseTimestamp={{this.timestamp}} | ||||||
|  |         /> | ||||||
|  |     `); | ||||||
|  |  | ||||||
|  |     await click('[data-test-attribution-export-button]'); | ||||||
|  |     await click(GENERAL.confirmButton); | ||||||
|  |     const [filename, ,] = this.csvDownloadStub.lastCall.args; | ||||||
|  |     assert.strictEqual(filename, 'clients_by_namespace'); | ||||||
|  |   }); | ||||||
| }); | }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 claire bontempo
					claire bontempo