Merge pull request #436 from hashicorp/navigate-into-a-target
View hosts in a target
This commit is contained in:
20 files changed
+291
-2
No files matched your search
@@ -104,6 +104,7 @@ host:
|
||||
session:
|
||||
title: Session
|
||||
title_plural: Sessions
|
||||
active_plural: Active Sessions
|
||||
description: A session is a connection to a target initiated through Boundary.
|
||||
messages:
|
||||
none:
|
||||
|
||||
@@ -13,7 +13,12 @@ Router.map(function () {
|
||||
this.route('method', { path: ':auth_method_id' });
|
||||
});
|
||||
this.route('projects', function () {
|
||||
this.route('targets', function () {});
|
||||
this.route('targets', function () {
|
||||
this.route('target', { path: ':target_id' }, function() {
|
||||
this.route('hosts');
|
||||
this.route('sessions');
|
||||
});
|
||||
});
|
||||
this.route('sessions', function() {});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,4 +25,5 @@ export default class ScopesScopeProjectsRoute extends Route {
|
||||
scope_id: currentScope.id,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import { all, hash } from 'rsvp';
|
||||
import { task, timeout } from 'ember-concurrency';
|
||||
import { A } from '@ember/array';
|
||||
import config from '../../../../config/environment';
|
||||
import { action } from '@ember/object';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
|
||||
|
||||
@@ -99,4 +101,17 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
|
||||
deactivate() {
|
||||
this.poller.cancelAll();
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Cancels the specified session and notifies user of success or error.
|
||||
* @param {SessionModel}
|
||||
*/
|
||||
@action
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.canceled-success')
|
||||
async cancelSession(session) {
|
||||
await session.cancelSession();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { A } from '@ember/array';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsTargetRoute extends Route {
|
||||
|
||||
// =services
|
||||
|
||||
@service session;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Load a target and it's active sessions
|
||||
* @param {object} params
|
||||
* @param {string} params.target_id
|
||||
* @return {{TargetModel, [SessionModel]}}
|
||||
*/
|
||||
async model({ target_id }) {
|
||||
const userId = this.session.data.authenticated.user_id;
|
||||
const target = await this.store.findRecord('target', target_id, { reload: true });
|
||||
const sessions = await this.store.query('session', { scope_id: target.scopeID });
|
||||
|
||||
// Filter to current user, current target, and active state
|
||||
const filteredSessions = sessions.filter(
|
||||
session => session.user_id === userId &&
|
||||
session.target_id === target.id &&
|
||||
session.isCancelable
|
||||
)
|
||||
// Sort sessions by time created
|
||||
const sortedSessions =
|
||||
A(filteredSessions).sortBy('session.created_time').reverse();
|
||||
return { target, sessions: sortedSessions };
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the target-specific templates for header and navigation
|
||||
* page sections.
|
||||
* @override
|
||||
*/
|
||||
renderTemplate() {
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render('scopes/scope/projects/targets/target/-header', {
|
||||
into: 'scopes/scope/projects/targets/target',
|
||||
outlet: 'header',
|
||||
});
|
||||
|
||||
this.render('scopes/scope/projects/targets/target/-navigation', {
|
||||
into: 'scopes/scope/projects/targets/target',
|
||||
outlet: 'navigation',
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { all } from 'rsvp';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsTargetHostsRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Loads all host-sets and associated hosts under the current target.
|
||||
* @return {Promise{[HostModel]}}
|
||||
*/
|
||||
async model() {
|
||||
const { target } = this.modelFor('scopes.scope.projects.targets.target');
|
||||
// Load all host-sets
|
||||
const hostSets = await all(
|
||||
target.host_sets.map(({ host_set_id }) =>
|
||||
this.store.findRecord('host-set', host_set_id)
|
||||
)
|
||||
);
|
||||
|
||||
// Extract host ids from all host sets
|
||||
const hostIds = hostSets.map(({host_ids}) =>
|
||||
host_ids.content.map((hostId) =>
|
||||
hostId.value
|
||||
)
|
||||
)
|
||||
|
||||
// Load unique hosts
|
||||
const uniqueHostIds = new Set(hostIds.flat());
|
||||
return all(
|
||||
[...uniqueHostIds].map((hostId) =>
|
||||
this.store.findRecord('host', hostId)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsTargetIndexRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Navigate to hosts within a target.
|
||||
*/
|
||||
afterModel() {
|
||||
this.transitionTo('scopes.scope.projects.targets.target.sessions');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { action } from '@ember/object';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsTargetSessionsRoute extends Route {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Loads all sessions for current target.
|
||||
* @return {Promise{[SessionModel]}}
|
||||
*/
|
||||
async model() {
|
||||
return this.modelFor('scopes.scope.projects.targets.target').sessions;
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Cancels the specified session and notifies user of success or error.
|
||||
* @param {SessionModel}
|
||||
*/
|
||||
@action
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.canceled-success')
|
||||
async cancelSession(session) {
|
||||
await session.cancelSession();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
<row.headerCell>{{t "form.proxy.label"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.started.label"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.status.label"}}</row.headerCell>
|
||||
<row.headerCell />
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
@@ -61,6 +62,13 @@
|
||||
<row.cell>
|
||||
<Rose::Badge>{{sessionAggregate.session.status}}</Rose::Badge>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
{{#if sessionAggregate.session.isCancelable}}
|
||||
<Rose::Button @style="secondary" {{on "click" (route-action "cancelSession" sessionAggregate.session)}}>
|
||||
{{t "actions.cancel"}}
|
||||
</Rose::Button>
|
||||
{{/if}}
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
{{#each @model as |model|}}
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
{{model.target.name}}
|
||||
<LinkTo
|
||||
@route="scopes.scope.projects.targets.target"
|
||||
@model={{model.target.id}}
|
||||
>
|
||||
{{model.target.displayName}}
|
||||
</LinkTo>
|
||||
<p>{{model.target.description}}</p>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{{page-title @model.target.displayName}}
|
||||
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.header>
|
||||
{{outlet "header"}}
|
||||
</page.header>
|
||||
|
||||
<page.navigation>
|
||||
{{outlet "navigation"}}
|
||||
</page.navigation>
|
||||
|
||||
<page.body>
|
||||
{{outlet}}
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -0,0 +1,4 @@
|
||||
<h2>
|
||||
{{@model.target.displayName}}
|
||||
</h2>
|
||||
<p>{{@model.target.description}}</p>
|
||||
@@ -0,0 +1,8 @@
|
||||
<Rose::Nav::Tabs as |nav|>
|
||||
<nav.link @route='scopes.scope.projects.targets.target.sessions'>
|
||||
{{t 'resources.session.active_plural'}}
|
||||
</nav.link>
|
||||
<nav.link @route='scopes.scope.projects.targets.target.hosts'>
|
||||
{{t 'resources.host.title_plural'}}
|
||||
</nav.link>
|
||||
</Rose::Nav::Tabs>
|
||||
@@ -0,0 +1,19 @@
|
||||
{{page-title (t "resources.host-set.title_plural")}}
|
||||
|
||||
<Rose::Table as |table|>
|
||||
<table.body as |body|>
|
||||
{{#each @model as |host|}}
|
||||
<body.row as |row|>
|
||||
<row.cell>
|
||||
{{host.displayName}}
|
||||
<p>{{host.description}}</p>
|
||||
</row.cell>
|
||||
<row.cell>{{host.id}}</row.cell>
|
||||
<row.cell>{{host.attributes.address}}</row.cell>
|
||||
<row.cell>
|
||||
<Rose::Badge>{{t (concat 'resources.host.types.' host.type)}}</Rose::Badge>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
@@ -0,0 +1 @@
|
||||
{{outlet}}
|
||||
@@ -0,0 +1,30 @@
|
||||
<Rose::Table as |table|>
|
||||
<table.body as |body|>
|
||||
{{#each @model as |session|}}
|
||||
<body.row as |row|>
|
||||
<row.cell class="nowrap">
|
||||
<Rose::Icon
|
||||
class={{concat "session-status-" session.status}}
|
||||
@name="app-icons/action-session"
|
||||
@size="medium" />
|
||||
{{session.id}}
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
<time datetime={{format-date-iso session.created_time}}>
|
||||
{{format-date-iso-human session.created_time}}
|
||||
</time>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
<Rose::Badge>{{session.status}}</Rose::Badge>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
{{#if session.isCancelable}}
|
||||
<Rose::Button @style="secondary" {{on "click" (route-action "cancelSession" session)}}>
|
||||
{{t "actions.cancel"}}
|
||||
</Rose::Button>
|
||||
{{/if}}
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/targets/target', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/targets/target');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/targets/target/hosts', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/targets/target/hosts');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/targets/target/index', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/targets/target/index');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/targets/target/sessions', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/targets/target/sessions');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user