diff --git a/addons/api/addon/models/host.js b/addons/api/addon/models/host.js index aa6ab14af..7a96180da 100644 --- a/addons/api/addon/models/host.js +++ b/addons/api/addon/models/host.js @@ -5,6 +5,7 @@ import GeneratedHostModel from '../generated/models/host'; import { attr } from '@ember-data/model'; +import { pluginTypes } from './host-catalog'; export default class HostModel extends GeneratedHostModel { // =attributes @@ -58,6 +59,14 @@ export default class HostModel extends GeneratedHostModel { return this.type === 'plugin'; } + /** + * True if the host catalog is an unknown type. + * @type {boolean} + */ + get isUnknown() { + return this.isPlugin && !pluginTypes.includes(this.plugin?.name); + } + /** * True if the host plugin type is AWS. * @type {boolean} diff --git a/addons/api/tests/unit/models/host-test.js b/addons/api/tests/unit/models/host-test.js index b385aa02e..6a6c92a7c 100644 --- a/addons/api/tests/unit/models/host-test.js +++ b/addons/api/tests/unit/models/host-test.js @@ -48,6 +48,28 @@ module('Unit | Model | host', function (hooks) { assert.false(modelStatic.isPlugin); }); + test('it has isUnknown and returns the expected values', async function (assert) { + assert.expect(4); + const store = this.owner.lookup('service:store'); + const modelAws = store.createRecord('host', { + type: 'plugin', + plugin: { name: 'aws' }, + }); + const modelAzure = store.createRecord('host', { + type: 'plugin', + plugin: { name: 'azure' }, + }); + const modelRandom = store.createRecord('host', { + type: 'plugin', + plugin: { name: 'random' }, + }); + + assert.strictEqual(typeof modelRandom.isUnknown, 'boolean'); + assert.true(modelRandom.isUnknown); + assert.false(modelAws.isUnknown); + assert.false(modelAzure.isUnknown); + }); + test('it has isAWS and returns the expected values', async function (assert) { assert.expect(3); const store = this.owner.lookup('service:store'); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index faa7655eb..f12baf9a3 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -295,6 +295,8 @@ session: actions: connect: Connect end: End session + host: Choose a Host + quick-connect: Quick Connect credential: title: Credential title_plural: Credentials diff --git a/ui/desktop/app/components/target/host-modal/index.hbs b/ui/desktop/app/components/target/host-modal/index.hbs new file mode 100644 index 000000000..9e1580d3b --- /dev/null +++ b/ui/desktop/app/components/target/host-modal/index.hbs @@ -0,0 +1,73 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: MPL-2.0 +}} + +{{#if @showModal}} + + + {{t 'resources.session.actions.host'}} + + + + + + + + +{{/if}} \ No newline at end of file diff --git a/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js b/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js index 4b977d8d6..64cc886c4 100644 --- a/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js +++ b/ui/desktop/app/controllers/scopes/scope/projects/targets/target.js @@ -4,11 +4,20 @@ */ import Controller from '@ember/controller'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; export default class ScopesScopeProjectsTargetsTargetController extends Controller { // =attributes queryParams = [{ isConnecting: { type: 'boolean' } }]; - isConnecting = false; + @tracked isConnecting = false; + + // =methods + + @action + toggle() { + this.isConnecting = !this.isConnecting; + } } diff --git a/ui/desktop/app/routes/scopes/scope/projects/targets/target.js b/ui/desktop/app/routes/scopes/scope/projects/targets/target.js index 534a845ec..efb1845f0 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/targets/target.js +++ b/ui/desktop/app/routes/scopes/scope/projects/targets/target.js @@ -71,9 +71,11 @@ export default class ScopesScopeProjectsTargetsTargetRoute extends Route { */ @action @loading - async preConnect(model) { + async preConnect(model, toggleModal) { if (model.target.address || model.hosts.length === 1) { await this.connect(model.target); + } else { + toggleModal(); } } diff --git a/ui/desktop/app/styles/app.scss b/ui/desktop/app/styles/app.scss index 9b8c217f9..337365cac 100644 --- a/ui/desktop/app/styles/app.scss +++ b/ui/desktop/app/styles/app.scss @@ -653,6 +653,38 @@ $chevron-down: url("data:image/svg+xml;utf-8, + { + this.set('called', true); + }; + toggle = () => { + this.set('called', true); + }; + store = this.owner.lookup('service:store'); + target = store.createRecord('target', { + name: 'User', + }); + hostA = store.createRecord('host', { + name: 'Host A', + compositeType: 'static', + address: '188e:68a9:b342:c05c:2595:2f46:499c:759f', + }); + hostB = store.createRecord('host', { + name: 'Host B', + compositeType: 'static', + description: 'Description of host B', + address: '188e:68a9:b342:c05c:2595:499c:759f:2f46', + }); + }); + + test('it renders', async function (assert) { + assert.expect(1); + this.set('connect', connect); + this.set('toggle', toggle); + this.set('hosts', [hostA, hostB]); + this.set('target', target); + + await render(hbs``); + + assert.dom('.hds-modal__tagline').hasText(target.displayName); + }); + + test('it closes model when dismissed', async function (assert) { + assert.expect(2); + const toggleModal = () => { + this.set('showModal', false); + }; + this.set('connect', connect); + this.set('toggle', toggleModal); + this.set('showModal', true); + this.set('hosts', [hostA, hostB]); + this.set('target', target); + + await render(hbs``); + + assert.dom('.hds-modal__tagline').hasText(target.displayName); + + await click('.hds-dismiss-button'); + + assert.dom('.hds-modal__tagline').doesNotExist(); + }); + + test('it calls connect when Quick Connect button clicked', async function (assert) { + assert.expect(1); + this.set('connect', connect); + this.set('toggle', toggle); + this.set('hosts', [hostA, hostB]); + this.set('target', target); + + await render(hbs``); + + await click('[data-test-host-quick-connect]'); + + assert.true(this.called); + }); + + test('it calls connect when host item is clicked', async function (assert) { + assert.expect(1); + this.set('connect', connect); + this.set('toggle', toggle); + this.set('hosts', [hostA, hostB]); + this.set('target', target); + + await render(hbs``); + + await click('[data-test-host-connect]'); + + assert.true(this.called); + }); +});