Merge pull request #199 from hashicorp/feature-sessions-api
Feature sessions api
This commit is contained in:
35 files changed
+537
-6
No files matched your search
@@ -0,0 +1,36 @@
|
||||
import BaseModel from '../../models/base';
|
||||
import { attr } from '@ember-data/model';
|
||||
|
||||
export default class GeneratedSessionModel extends BaseModel {
|
||||
|
||||
// =attributes
|
||||
|
||||
@attr('string') user_id;
|
||||
@attr('string') target_id;
|
||||
@attr('string') host_id;
|
||||
@attr('string') host_set_id;
|
||||
|
||||
@attr('string', {
|
||||
description: 'The name of the current status of this session.',
|
||||
readOnly: true,
|
||||
})
|
||||
status;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was created\nOutput only.',
|
||||
readOnly: true,
|
||||
})
|
||||
created_time;
|
||||
|
||||
@attr('date', {
|
||||
description: 'The time this resource was last updated\nOutput only.',
|
||||
readOnly: true,
|
||||
})
|
||||
updated_time;
|
||||
|
||||
@attr('number', {
|
||||
description: 'Current version number of this resource.',
|
||||
})
|
||||
version;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import GeneratedSessionModel from '../generated/models/session';
|
||||
|
||||
export default class SessionModel extends GeneratedSessionModel {
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Cancels the session via the `cancel` method.
|
||||
* See serializer and adapter for more information.
|
||||
* @param {object} options
|
||||
* @param {object} options.adapterOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
cancelSession(options = { adapterOptions: {} }) {
|
||||
const defaultAdapterOptions = {
|
||||
method: 'cancel',
|
||||
};
|
||||
return this.save({
|
||||
...options,
|
||||
adapterOptions: {
|
||||
...defaultAdapterOptions,
|
||||
...options.adapterOptions,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import ApplicationSerializer from './application';
|
||||
|
||||
export default class SessionSerializer extends ApplicationSerializer {
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* If `adapterOptions.method` is `cancel`, the serialization should
|
||||
* include the version field only.
|
||||
* @override
|
||||
* @param {Snapshot} snapshot
|
||||
* @return {object}
|
||||
*/
|
||||
serialize(snapshot) {
|
||||
let serialized = super.serialize(...arguments);
|
||||
if (snapshot?.adapterOptions?.method === 'cancel') {
|
||||
serialized = this.serializeForCancel(snapshot);
|
||||
}
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a payload containing only version
|
||||
* @param {Snapshot} snapshot
|
||||
* @return {object}
|
||||
*/
|
||||
serializeForCancel(snapshot) {
|
||||
return {
|
||||
version: snapshot.attr('version'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/models/session';
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'api/serializers/session';
|
||||
@@ -0,0 +1,13 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Model | session', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let model = store.createRecord('session', {});
|
||||
assert.ok(model);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Serializer | session', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let store = this.owner.lookup('service:store');
|
||||
let serializer = store.serializerFor('session');
|
||||
|
||||
assert.ok(serializer);
|
||||
});
|
||||
|
||||
test('it serializes only version when `adapterOptions.method` is `cancel`', function (assert) {
|
||||
assert.expect(1);
|
||||
const store = this.owner.lookup('service:store');
|
||||
const serializer = store.serializerFor('session');
|
||||
const record = store.createRecord('session', {
|
||||
version: 1,
|
||||
});
|
||||
const snapshot = record._createSnapshot();
|
||||
snapshot.adapterOptions = {
|
||||
method: 'cancel',
|
||||
};
|
||||
const serializedRecord = serializer.serialize(snapshot);
|
||||
assert.deepEqual(serializedRecord, {
|
||||
version: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -54,3 +54,7 @@ p {
|
||||
a {
|
||||
color: var(--action);
|
||||
}
|
||||
|
||||
time {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
@@ -34,4 +34,8 @@
|
||||
padding: sizing.rems(l) sizing.rems(s);
|
||||
}
|
||||
}
|
||||
|
||||
[class*="rose-button"] {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,15 @@ export default {
|
||||
},
|
||||
},
|
||||
date: {
|
||||
default: {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
hour12: false
|
||||
},
|
||||
hhmmss: {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export default helper(function formatDateIsoHuman(params/*, hash*/) {
|
||||
return params[0].toISOString()
|
||||
.replace('T', ' ')
|
||||
.replace(/\.\d*Z/, '');
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { helper } from '@ember/component/helper';
|
||||
|
||||
export default helper(function formatDateIso(params/*, hash*/) {
|
||||
return params[0].toISOString();
|
||||
});
|
||||
@@ -52,6 +52,7 @@ Router.map(function () {
|
||||
});
|
||||
this.route('new');
|
||||
});
|
||||
this.route('sessions', function() {});
|
||||
});
|
||||
this.route('new');
|
||||
});
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { all, hash } from 'rsvp';
|
||||
import { task, timeout } from 'ember-concurrency';
|
||||
|
||||
const POLL_TIMEOUT_SECONDS = 2.5;
|
||||
|
||||
export default class ScopesScopeProjectsProjectSessionsRoute extends Route {
|
||||
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
@service notify;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* A simple Ember Concurrency-based polling task that refreshes the route
|
||||
* every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes
|
||||
* to session `status` that may occur.
|
||||
*
|
||||
* NOTE: tasks are sort of attributes and sort of methods, but they are not
|
||||
* language-level constructs. Thus we annotate this task as if it
|
||||
* is an attribute.
|
||||
* @type {Task}
|
||||
*/
|
||||
@task(function * () {
|
||||
while(true) {
|
||||
yield timeout(POLL_TIMEOUT_SECONDS * 1000);
|
||||
yield this.refresh();
|
||||
}
|
||||
}).drop() poller;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Loads all sessions under the current scope and encapsulates them into
|
||||
* an array of objects containing their associated users and targets.
|
||||
* @return {Promise{[{session: SessionModel, user: UserModel, target: TargetModel}]}}
|
||||
*/
|
||||
async model() {
|
||||
const { id: scope_id } = this.modelFor('scopes.scope.projects.project');
|
||||
const sessions = await this.store.query('session', { scope_id });
|
||||
return all(
|
||||
sessions.map(session => hash({
|
||||
session,
|
||||
user: this.store.findRecord('user', session.user_id),
|
||||
target: this.store.findRecord('target', session.target_id),
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* When this route is activated (entered), begin polling for changes.
|
||||
*/
|
||||
activate() {
|
||||
this.poller.perform();
|
||||
}
|
||||
|
||||
/**
|
||||
* When this route is deactivated (exited), stop polling for changes.
|
||||
*/
|
||||
deactivate() {
|
||||
this.poller.cancelAll();
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Cancels the specified session and notifies user of success or error.
|
||||
* @param {SessionModel}
|
||||
*/
|
||||
@action
|
||||
async cancelSession(session) {
|
||||
try {
|
||||
await session.cancelSession();
|
||||
this.notify.success(this.intl.t('notify.session-canceled'));
|
||||
} catch (error) {
|
||||
// TODO: replace with translated strings
|
||||
this.notify.error(error.message, { closeAfter: null });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import Route from '@ember/routing/route';
|
||||
|
||||
export default class ScopesScopeProjectsProjectSessionsIndexRoute extends Route {
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<header.row as |row|>
|
||||
<row.headerCell>{{t 'form.label.name'}}</row.headerCell>
|
||||
<row.headerCell>{{t 'form.label.id'}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.actions"}}</row.headerCell>
|
||||
<row.headerCell>{{t "titles.actions"}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<Rose::Nav::Sidebar @title={{t "titles.resources"}} as |nav|>
|
||||
<nav.link @route="scopes.scope.projects.project.sessions">
|
||||
{{t "resources.sessions"}}
|
||||
</nav.link>
|
||||
<nav.link @route="scopes.scope.projects.project.targets">
|
||||
{{t "resources.targets"}}
|
||||
</nav.link>
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<row.headerCell>{{t "form.label.name"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.type"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.id"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.actions"}}</row.headerCell>
|
||||
<row.headerCell>{{t "titles.actions"}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{{title (t "resources.sessions")}}
|
||||
|
||||
{{outlet}}
|
||||
@@ -0,0 +1,66 @@
|
||||
<Rose::Layout::Page as |page|>
|
||||
|
||||
<page.breadcrumbs>
|
||||
<BreadCrumbs />
|
||||
</page.breadcrumbs>
|
||||
|
||||
<page.header>
|
||||
<h2>{{t "resources.sessions"}}</h2>
|
||||
</page.header>
|
||||
|
||||
<page.body>
|
||||
|
||||
{{#if @model}}
|
||||
|
||||
<Rose::Table as |table|>
|
||||
<table.header as |header|>
|
||||
<header.row as |row|>
|
||||
<row.headerCell>{{t "form.label.id"}}</row.headerCell>
|
||||
<row.headerCell>{{t "resources.user"}}</row.headerCell>
|
||||
<row.headerCell>{{t "resources.target"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.started"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.status"}}</row.headerCell>
|
||||
<row.headerCell>{{t "titles.actions"}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
{{#each @model as |sessionAggregate|}}
|
||||
<body.row as |row|>
|
||||
<row.headerCell>
|
||||
<code>{{sessionAggregate.session.id}}</code>
|
||||
</row.headerCell>
|
||||
<row.cell>{{sessionAggregate.user.displayName}}</row.cell>
|
||||
<row.cell>{{sessionAggregate.target.displayName}}</row.cell>
|
||||
<row.cell>
|
||||
<time datetime={{format-date-iso sessionAggregate.session.created_time}}>
|
||||
{{format-date-iso-human sessionAggregate.session.created_time}}
|
||||
</time>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
<Rose::Badge>{{sessionAggregate.session.status}}</Rose::Badge>
|
||||
</row.cell>
|
||||
<row.cell>
|
||||
<Rose::Button @style="secondary" {{on "click" (route-action "cancelSession" sessionAggregate.session)}}>
|
||||
{{t "actions.cancel"}}
|
||||
</Rose::Button>
|
||||
</row.cell>
|
||||
</body.row>
|
||||
{{/each}}
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
|
||||
{{else}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message @title={{t "titles.no-sessions"}} as |message|>
|
||||
<message.description>
|
||||
{{t "descriptions.no-sessions"}}
|
||||
</message.description>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
{{/if}}
|
||||
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -8,7 +8,7 @@
|
||||
<row.headerCell>{{t "form.label.name"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.type"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.id"}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.actions"}}</row.headerCell>
|
||||
<row.headerCell>{{t "titles.actions"}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<row.headerCell>{{t 'form.label.name'}}</row.headerCell>
|
||||
<row.headerCell>{{t 'form.label.type'}}</row.headerCell>
|
||||
<row.headerCell>{{t 'form.label.id'}}</row.headerCell>
|
||||
<row.headerCell>{{t "form.label.actions"}}</row.headerCell>
|
||||
<row.headerCell>{{t "titles.actions"}}</row.headerCell>
|
||||
</header.row>
|
||||
</table.header>
|
||||
<table.body as |body|>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import config from '../config/environment';
|
||||
import { authHandler, deauthHandler } from './route-handlers/auth';
|
||||
// Uncomment to use in simulating error responses
|
||||
// import { Response } from 'miragejs';
|
||||
import { pickRandomStatusString } from './factories/session';
|
||||
//import { Response } from 'miragejs';
|
||||
|
||||
export default function() {
|
||||
|
||||
@@ -294,6 +294,38 @@ export default function() {
|
||||
return target.update(updatedAttrs);
|
||||
});
|
||||
|
||||
// session
|
||||
|
||||
// To simulate changes to `session.status` that may occur in the backend,
|
||||
// we quietly randomize the value of the field on GET.
|
||||
this.get('/sessions', function ({ sessions }, { queryParams: { scope_id } }) {
|
||||
sessions.where(session => session.scopeId === scope_id)
|
||||
.models
|
||||
.forEach(session => session.update({
|
||||
status: pickRandomStatusString()
|
||||
}));
|
||||
return sessions.where(session => session.scopeId === scope_id)
|
||||
});
|
||||
this.get('/sessions/:id', function ({ sessions }, { params: { id } }) {
|
||||
const session = sessions.find(id);
|
||||
return session.update({
|
||||
status: pickRandomStatusString()
|
||||
});
|
||||
});
|
||||
this.post('/sessions/:idMethod', function ({ sessions }, { params: { idMethod } }) {
|
||||
const attrs = this.normalizedRequestAttrs();
|
||||
const id = idMethod.split(':')[0];
|
||||
const method = idMethod.split(':')[1];
|
||||
const session = sessions.find(id);
|
||||
const updatedAttrs = {
|
||||
version: attrs.version
|
||||
};
|
||||
if (method === 'cancel') {
|
||||
updatedAttrs.status = 'canceling';
|
||||
}
|
||||
return session.update(updatedAttrs);
|
||||
});
|
||||
|
||||
/* Uncomment the following line and the Response import above
|
||||
* Then change the response code to simulate error responses.
|
||||
* this.get('/scopes', () => new Response(505));
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import factory from '../generated/factories/target';
|
||||
import { trait } from 'ember-cli-mirage';
|
||||
import { random } from 'faker';
|
||||
|
||||
const statusStrings = [
|
||||
'pending',
|
||||
'active',
|
||||
'canceling',
|
||||
'terminated'
|
||||
];
|
||||
|
||||
export const pickRandomStatusString = () => random.arrayElement(statusStrings);
|
||||
|
||||
export default factory.extend({
|
||||
|
||||
id: (i) => `session-id-${i}`,
|
||||
status: pickRandomStatusString,
|
||||
|
||||
/**
|
||||
* Sessions generated with this trait will automatically form
|
||||
* associations to user, target, host set, and host (if available) within
|
||||
* the current scope.
|
||||
*/
|
||||
withAssociations: trait({
|
||||
afterCreate(record, server) {
|
||||
const scopeId = record.scopeId;
|
||||
if (record.scope.type === 'project') {
|
||||
const orgScopeId = record.scope?.scope.id;
|
||||
const user =
|
||||
server.schema.users.where({ scopeId: orgScopeId }).models[0];
|
||||
if (user) record.update({ user });
|
||||
}
|
||||
const target = server.schema.targets.where({ scopeId }).models[0];
|
||||
if (target) {
|
||||
record.update({ target });
|
||||
const hostSet = target.hostSets.models[0];
|
||||
if (hostSet) {
|
||||
record.update({ hostSet });
|
||||
const host = hostSet.hosts.models[0];
|
||||
if (host) record.update({ host });
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { Factory } from 'ember-cli-mirage';
|
||||
import { random, date } from 'faker';
|
||||
|
||||
/**
|
||||
* GeneratedSessionModelFactory
|
||||
*/
|
||||
export default Factory.extend({
|
||||
|
||||
type: 'tcp',
|
||||
created_time: () => date.recent(),
|
||||
updated_time: () => date.recent(),
|
||||
version: () => random.number(),
|
||||
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Model, belongsTo } from 'ember-cli-mirage';
|
||||
|
||||
export default Model.extend({
|
||||
|
||||
scope: belongsTo(),
|
||||
user: belongsTo(),
|
||||
target: belongsTo(),
|
||||
host: belongsTo(),
|
||||
hostSet: belongsTo(),
|
||||
|
||||
});
|
||||
@@ -26,6 +26,7 @@ export default function(server) {
|
||||
server.schema.scopes.where({type: 'project'}).models.forEach(scope => {
|
||||
server.createList('host-catalog', 2, { scope }, 'withChildren');
|
||||
server.createList('target', 2, { scope }, 'withRandomHostSets');
|
||||
server.createList('session', 4, { scope }, 'withAssociations');
|
||||
});
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import ApplicationSerializer from './application';
|
||||
import { underscore } from '@ember/string';
|
||||
|
||||
export default ApplicationSerializer.extend({
|
||||
modelName: 'session',
|
||||
|
||||
keyForRelationshipId(relationshipName) {
|
||||
return `${this._container.inflector.singularize(
|
||||
underscore(relationshipName)
|
||||
)}_id`;
|
||||
},
|
||||
|
||||
_hashForModel(model) {
|
||||
const json = ApplicationSerializer.prototype._hashForModel.apply(this, arguments);
|
||||
json.user_id = model.userId;
|
||||
json.target_id = model.targetId;
|
||||
json.host_set_id = model.hostSetId;
|
||||
json.host_id = model.hostId;
|
||||
return json;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -54,6 +54,7 @@
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-template-lint": "^1.0.0-beta.3",
|
||||
"ember-cli-uglify": "^3.0.0",
|
||||
"ember-concurrency": "^1.3.0",
|
||||
"ember-data": "~3.16.0",
|
||||
"ember-data-model-fragments": "5.0.0-beta.0",
|
||||
"ember-exam": "^5.0.1",
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Helper | format-date-iso-human', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it renders', async function(assert) {
|
||||
this.set('inputValue', new Date('2020-01-01T00:00:00.999Z'));
|
||||
|
||||
await render(hbs`{{format-date-iso-human inputValue}}`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '2020-01-01 00:00:00');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Helper | format-date-iso', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it renders', async function(assert) {
|
||||
this.set('inputValue', new Date('2020-01-01T00:00:00.999Z'));
|
||||
|
||||
await render(hbs`{{format-date-iso inputValue}}`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '2020-01-01T00:00:00.999Z');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/project/sessions', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/project/sessions');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/project/sessions/index', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/project/sessions/index');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
@@ -77,6 +77,8 @@ resources:
|
||||
targets: Targets
|
||||
accounts: Accounts
|
||||
account: Account
|
||||
sessions: Sessions
|
||||
session: Session
|
||||
titles:
|
||||
iam: IAM
|
||||
new: New
|
||||
@@ -114,6 +116,8 @@ titles:
|
||||
accounts: Accounts
|
||||
account: Account
|
||||
accounts-welcome: Welcome to Accounts
|
||||
no-sessions: No Active Sessions
|
||||
actions: Actions
|
||||
descriptions:
|
||||
host-catalogs: Overview of collections of hosts and host sets.
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
@@ -136,6 +140,7 @@ descriptions:
|
||||
no-available-users: No users are available to add to this group.
|
||||
account-set-password: Set a password for the account.
|
||||
accounts-welcome: No accounts are available in this auth method.
|
||||
no-sessions: There are no active sessions at this time.
|
||||
states:
|
||||
info: Info
|
||||
success: Success
|
||||
@@ -157,6 +162,8 @@ form:
|
||||
default_port: Default Port
|
||||
current_password: Current Password
|
||||
new_password: New Password
|
||||
started: Started
|
||||
status: Status
|
||||
errors:
|
||||
401:
|
||||
title: You are not signed in
|
||||
@@ -180,3 +187,4 @@ notify:
|
||||
create-success: Created succesfully.
|
||||
save-success: Saved succesfully.
|
||||
delete-success: Deleted successfully.
|
||||
session-canceled: Session canceled.
|
||||
@@ -8918,6 +8918,15 @@ ember-compatibility-helpers@^1.1.1, ember-compatibility-helpers@^1.1.2, ember-co
|
||||
ember-cli-version-checker "^2.1.1"
|
||||
semver "^5.4.1"
|
||||
|
||||
ember-concurrency@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-concurrency/-/ember-concurrency-1.3.0.tgz#66f90fb792687470bcee1172adc0ebf33f5e8b9c"
|
||||
integrity sha512-DwGlfWFpYyAkTwsedlEtK4t1DznJSculAW6Vq5S1C0shVPc5b6tTpHB2FFYisannSYkm+wpm1f1Pd40qiNPtOQ==
|
||||
dependencies:
|
||||
ember-cli-babel "^7.7.3"
|
||||
ember-compatibility-helpers "^1.2.0"
|
||||
ember-maybe-import-regenerator "^0.1.6"
|
||||
|
||||
ember-cookies@^0.5.0, ember-cookies@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/ember-cookies/-/ember-cookies-0.5.2.tgz#06e33463f2f83254fefaf224cc944dec3fb9d3ba"
|
||||
|
||||
Reference in new issue
Block a user