diff --git a/addons/api/addon/generated/models/session.js b/addons/api/addon/generated/models/session.js index e9c9c4fd1..98335a31d 100644 --- a/addons/api/addon/generated/models/session.js +++ b/addons/api/addon/generated/models/session.js @@ -32,6 +32,13 @@ export default class GeneratedSessionModel extends BaseModel { }) updated_time; + @attr('date', { + description: + 'After this time the connection will be expired, e.g. forcefully terminated.\nOutput only.', + readOnly: true, + }) + expiration_time; + @attr('number', { description: 'Current version number of this resource.', }) diff --git a/addons/api/mirage/generated/factories/session.js b/addons/api/mirage/generated/factories/session.js index 2e3897e35..915a5320a 100644 --- a/addons/api/mirage/generated/factories/session.js +++ b/addons/api/mirage/generated/factories/session.js @@ -13,5 +13,6 @@ export default Factory.extend({ type: 'tcp', created_time: () => faker.date.recent(), updated_time: () => faker.date.recent(), + expiration_time: () => faker.date.soon(), version: () => faker.number.int(), }); diff --git a/addons/core/addon/helpers/time-remaining.js b/addons/core/addon/helpers/time-remaining.js new file mode 100644 index 000000000..6fac4d9ef --- /dev/null +++ b/addons/core/addon/helpers/time-remaining.js @@ -0,0 +1,37 @@ +import Helper from '@ember/component/helper'; +import { inject as service } from '@ember/service'; +import DurationUnitFormat from 'intl-unofficial-duration-unit-format'; + +export default class extends Helper { + // =services + @service clockTick; + @service intl; + + compute([expiration_time]) { + if (!expiration_time) return; + + // Parse the expiration time + const expirationTime = new Date(expiration_time); + + // Get the curent time + const currentTime = this.clockTick.now; + + // Calculate the difference in milleseconds + const difference = expirationTime - currentTime; + + // Calculate the difference in seconds if the difference is positive + const differenceInSeconds = + difference > 0 ? Math.floor(difference / 1000) : 0; + + // Format the duration + const duration = new DurationUnitFormat(this.intl.primaryLocale, { + style: 'timer', + format: `{hours}:{minutes}:{seconds} ${this.intl.t( + 'resources.session.remaining', + )}`, + }); + + // Return the formatted duration + return duration.format(differenceInSeconds); + } +} diff --git a/addons/core/app/helpers/time-remaining.js b/addons/core/app/helpers/time-remaining.js new file mode 100644 index 000000000..403e78c07 --- /dev/null +++ b/addons/core/app/helpers/time-remaining.js @@ -0,0 +1,6 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +export { default } from 'core/helpers/time-remaining'; diff --git a/addons/core/tests/integration/helpers/time-remaining-test.js b/addons/core/tests/integration/helpers/time-remaining-test.js new file mode 100644 index 000000000..90e4cd93b --- /dev/null +++ b/addons/core/tests/integration/helpers/time-remaining-test.js @@ -0,0 +1,30 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import hbs from 'htmlbars-inline-precompile'; +import { setupIntl } from 'ember-intl/test-support'; + +module('Integration | Helper | time-remaining', function (hooks) { + setupRenderingTest(hooks); + setupIntl(hooks); + + test('it calculates the correct time remaining', async function (assert) { + assert.expect(1); + + this.set('expirationTime', new Date(Date.now() + 1000 * 60 * 60 * 24)); + + await render(hbs`{{time-remaining this.expirationTime}}`); + + assert.strictEqual(this.element.textContent.trim(), '23:59:59 remaining'); + }); + + test('it handles negative time remaining', async function (assert) { + assert.expect(1); + + this.set('expirationTime', new Date(Date.now() - 1000 * 60 * 60 * 24)); + + await render(hbs`{{time-remaining this.expirationTime}}`); + + assert.strictEqual(this.element.textContent.trim(), '0:00:00 remaining'); + }); +}); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 2ea9cb06d..e47949d60 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -286,6 +286,7 @@ session: title_plural: Sessions active_plural: Active Sessions description: A session is a connection to a target initiated through Boundary. + remaining: remaining status: active: Active pending: Pending