/** * Copyright (c) HashiCorp, Inc. * SPDX-License-Identifier: BUSL-1.1 */ import { action } from '@ember/object'; import { service } from '@ember/service'; import { waitFor } from '@ember/test-waiters'; import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { parseAPITimestamp } from 'core/utils/date-formatters'; import { sanitizePath } from 'core/utils/sanitize-path'; import { isSameMonth } from 'date-fns'; import { task } from 'ember-concurrency'; /** * @module ClientsPageHeader * ClientsPageHeader components are used to render a header and check for export capabilities before rendering an export button. * * @example * ```js * * ``` * @param {string} [billingStartTime] - ISO timestamp of billing start date, to be passed to date picker * @param {string} [activityTimestamp] - ISO timestamp created in serializer to timestamp the response to be displayed in page header * @param {string} [startTimestamp] - ISO timestamp of start time, to be passed to export request * @param {string} [endTimestamp] - ISO timestamp of end time, to be passed to export request * @param {number} [retentionMonths = 48] - number of months for historical billing, to be passed to date picker * @param {string} [namespace] - namespace filter. Will be appended to the current namespace in the export request. * @param {string} [upgradesDuringActivity] - array of objects containing version history upgrade data * @param {boolean} [noData = false] - when true, export button will hide regardless of capabilities * @param {function} [onChange] - callback when a new date range is saved, to be passed to date picker */ export default class ClientsPageHeaderComponent extends Component { @service download; @service namespace; @service store; @service version; @tracked canDownload = false; @tracked showEditModal = false; @tracked showExportModal = false; @tracked exportFormat = 'csv'; @tracked downloadError = ''; constructor() { super(...arguments); this.getExportCapabilities(this.args.namespace); } get showExportButton() { if (this.args.noData === true) return false; return this.canDownload; } @waitFor async getExportCapabilities(ns = '') { try { // selected namespace usually ends in / const url = ns ? `${sanitizePath(ns)}/sys/internal/counters/activity/export` : 'sys/internal/counters/activity/export'; const cap = await this.store.findRecord('capabilities', url); this.canDownload = cap.canSudo; } catch (e) { // if we can't read capabilities, default to show this.canDownload = true; } } get formattedStartDate() { if (!this.args.startTimestamp) return null; return parseAPITimestamp(this.args.startTimestamp, 'MMMM yyyy'); } get formattedEndDate() { if (!this.args.endTimestamp) return null; return parseAPITimestamp(this.args.endTimestamp, 'MMMM yyyy'); } get showEndDate() { // displays on CSV export modal, no need to display duplicate months and years if (!this.args.endTimestamp) return false; const startDateObject = parseAPITimestamp(this.args.startTimestamp); const endDateObject = parseAPITimestamp(this.args.endTimestamp); return !isSameMonth(startDateObject, endDateObject); } get formattedCsvFileName() { const endRange = this.showEndDate ? `-${this.formattedEndDate}` : ''; const csvDateRange = this.formattedStartDate ? `_${this.formattedStartDate + endRange}` : ''; const ns = this.namespaceFilter ? `_${this.namespaceFilter}` : ''; return `clients_export${ns}${csvDateRange}`; } get namespaceFilter() { const currentNs = this.namespace.path; const { namespace } = this.args; return namespace ? sanitizePath(`${currentNs}/${namespace}`) : sanitizePath(currentNs); } get showCommunity() { return this.version.isCommunity && !!this.formattedStartDate && !!this.formattedEndDate; } async getExportData() { const adapter = this.store.adapterFor('clients/activity'); const { startTimestamp, endTimestamp } = this.args; return adapter.exportData({ // the API only accepts json or csv format: this.exportFormat === 'jsonl' ? 'json' : 'csv', start_time: startTimestamp, end_time: endTimestamp, namespace: this.namespaceFilter, }); } parseAPITimestamp = (timestamp, format) => { return parseAPITimestamp(timestamp, format); }; exportChartData = task({ drop: true }, async (filename) => { try { const contents = await this.getExportData(); this.download.download(filename, contents, this.exportFormat); this.showExportModal = false; } catch (e) { this.downloadError = e.message; } }); @action setExportFormat(evt) { const { value } = evt.target; this.exportFormat = value; } @action resetModal() { this.showExportModal = false; this.downloadError = ''; } @action setEditModalVisible(visible) { this.showEditModal = visible; } }