feat: Create and assign host to host set in a single action
This commit is contained in:
14 files changed
+317
-15
No files matched your search
@@ -43,9 +43,9 @@
|
||||
|
||||
{{#unless this.hasAvailableHosts}}
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message @title={{t "resources.host-set.host.messages.add.title"}} as |message|>
|
||||
<Rose::Message @title={{t "resources.host-set.host.messages.add-none.title"}} as |message|>
|
||||
<message.description>
|
||||
{{t "resources.host-set.host.messages.add.description"}}
|
||||
{{t "resources.host-set.host.messages.add-none.description"}}
|
||||
</message.description>
|
||||
<message.link @route="scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts">
|
||||
<Rose::Icon @name="chevron-left" />
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.host.isSaving}}
|
||||
@error={{@model.host.errors.base}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<form.input
|
||||
@value={{@model.hostSet.displayName}}
|
||||
@label={{t "form.host_set.label"}}
|
||||
readonly={{true}}
|
||||
@disabled={{true}} />
|
||||
|
||||
<form.input
|
||||
@value={{@model.host.type}}
|
||||
@label={{t "form.type.label"}}
|
||||
readonly={{true}}
|
||||
@disabled={{true}} />
|
||||
|
||||
<form.input
|
||||
@name="name"
|
||||
@type="name"
|
||||
@value={{@model.host.name}}
|
||||
@label={{t "form.name.label"}}
|
||||
@error={{@model.host.errors.name}}
|
||||
@helperText={{t "form.name.help"}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.host.errors.name}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.host.errors.name as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.textarea
|
||||
@name="description"
|
||||
@type="description"
|
||||
@value={{@model.host.description}}
|
||||
@label={{t "form.description.label"}}
|
||||
@error={{@model.host.errors.description}}
|
||||
@helperText={{t "form.description.help"}}
|
||||
disabled={{@model.host.isSaving}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.host.errors.description}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.host.errors.description as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.textarea>
|
||||
|
||||
<form.input
|
||||
@name="address"
|
||||
@type="text"
|
||||
@value={{@model.host.attributes.address}}
|
||||
@label={{t "form.address.label"}}
|
||||
@error={{@model.host.errors.name}}
|
||||
@helperText={{t "form.address.help"}}
|
||||
readonly={{false}}
|
||||
as |field|>
|
||||
{{#if @model.host.errors.name}}
|
||||
<field.errors as |errors|>
|
||||
{{#each @model.host.errors.name as |error|}}
|
||||
<errors.message>{{error.message}}</errors.message>
|
||||
{{/each}}
|
||||
</field.errors>
|
||||
{{/if}}
|
||||
</form.input>
|
||||
|
||||
<form.actions
|
||||
@disabled={{if @model.host.cannotSave @model.host.cannotSave}}
|
||||
@enableEditText={{t "actions.edit-form"}}
|
||||
@submitText={{t "actions.save"}}
|
||||
@cancelText={{t "actions.cancel"}} />
|
||||
|
||||
</Rose::Form>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHostSetCreateHostController extends Controller {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Translated new host set breadcrumb
|
||||
* @type {string}
|
||||
*/
|
||||
get breadCrumb() {
|
||||
return this.intl.t('resources.host-set.actions.create-host');
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ Router.map(function () {
|
||||
this.route('host-set', { path: ':host_set_id' }, function () {
|
||||
this.route('hosts');
|
||||
this.route('add-hosts');
|
||||
this.route('create-host');
|
||||
});
|
||||
this.route('new');
|
||||
});
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { hash } from 'rsvp';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { notifySuccess, notifyError } from '../../../../../../../../../decorators/notify';
|
||||
|
||||
export default class ScopesScopeProjectsProjectHostCatalogsHostCatalogHostSetsHostSetCreateHostRoute extends Route {
|
||||
// =services
|
||||
|
||||
@service intl;
|
||||
@service notify;
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Creates a new unsaved host in current host catalog scope.
|
||||
* @return {Promise{HostSetModel,HostModel}}
|
||||
*/
|
||||
async model() {
|
||||
const { id: host_catalog_id } = this.modelFor(
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog'
|
||||
);
|
||||
|
||||
return hash({
|
||||
hostSet: this.modelFor(
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set'
|
||||
),
|
||||
host: this.store.createRecord('host', {
|
||||
type: 'static',
|
||||
host_catalog_id,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the create-host-specific header template.
|
||||
* Empties the actions and navigation outlets and renders a custom empty header.
|
||||
* @override
|
||||
*/
|
||||
renderTemplate() {
|
||||
super.renderTemplate(...arguments);
|
||||
|
||||
this.render(
|
||||
'scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/create-host/-header',
|
||||
{
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'header',
|
||||
}
|
||||
);
|
||||
|
||||
this.render('-empty', {
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'navigation',
|
||||
});
|
||||
|
||||
this.render('-empty', {
|
||||
into: 'scopes/scope/projects/project/host-catalogs/host-catalog',
|
||||
outlet: 'actions',
|
||||
});
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Saves host and add it to current host set.
|
||||
* @param {HostSetModel,HostModel} model
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.add-success')
|
||||
async save(model) {
|
||||
await model.host.save();
|
||||
await model.hostSet.addHost(model.host.id);
|
||||
await this.replaceWith(
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect to hosts in host set as if nothing ever happened.
|
||||
*/
|
||||
@action
|
||||
cancel() {
|
||||
this.replaceWith(
|
||||
'scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.hosts'
|
||||
);
|
||||
}
|
||||
}
|
||||
+3
@@ -1,4 +1,7 @@
|
||||
<Rose::Dropdown @text="{{t 'actions.manage'}}" @dropdownRight={{true}} as |dropdown|>
|
||||
<dropdown.link @route="scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.create-host">
|
||||
{{t 'resources.host-set.actions.create-host'}}
|
||||
</dropdown.link>
|
||||
<dropdown.link @route="scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.add-hosts">
|
||||
{{t 'resources.host-set.actions.add-hosts'}}
|
||||
</dropdown.link>
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{{title (t 'resources.host-set.actions.create-host')}}
|
||||
|
||||
<Form::HostSet::CreateHost
|
||||
@model={{@model}}
|
||||
@submit={{route-action "save" @model}}
|
||||
@cancel={{route-action "cancel" @model}}
|
||||
/>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<h2>
|
||||
{{t "resources.host-set.actions.create-host"}}
|
||||
<DocLink @doc="host.new" @iconSize="large" />
|
||||
</h2>
|
||||
+2
-5
@@ -62,12 +62,9 @@
|
||||
<message.description>
|
||||
{{t "resources.host-set.host.messages.none.description"}}
|
||||
</message.description>
|
||||
<message.link @route="scopes.scope.projects.project.host-catalogs.host-catalog.hosts.new">
|
||||
<message.link @route="scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.create-host">
|
||||
<Rose::Icon @name="plus-circle-outline" />
|
||||
{{t "resources.host-set.host.messages.none.actions.new"}}
|
||||
</message.link>
|
||||
<message.link @route="scopes.scope.projects.project.host-catalogs.host-catalog.host-sets.host-set.add-hosts">
|
||||
{{t "resources.host-set.actions.add-hosts"}}
|
||||
{{t "resources.host-set.actions.create-host"}}
|
||||
</message.link>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
||||
import { visit, currentURL, click, find, findAll, fillIn } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
@@ -32,7 +32,9 @@ module('Acceptance | host-sets | hosts', function (hooks) {
|
||||
hostCatalog: null,
|
||||
hostSets: null,
|
||||
hostSet: null,
|
||||
hostSetHosts: null
|
||||
hostSetHosts: null,
|
||||
addHosts: null,
|
||||
newHost: null,
|
||||
};
|
||||
let hostSetHostsCount;
|
||||
|
||||
@@ -62,6 +64,7 @@ module('Acceptance | host-sets | hosts', function (hooks) {
|
||||
urls.hostSet = `${urls.hostSets}/${instances.hostSet.id}`;
|
||||
urls.hostSetHosts = `${urls.hostSet}/hosts`;
|
||||
urls.addHosts = `${urls.hostSet}/add-hosts`;
|
||||
urls.newHost = `${urls.hostSet}/create-host`;
|
||||
// Counts
|
||||
hostSetHostsCount = instances.hostSet.hosts.length;
|
||||
});
|
||||
@@ -114,7 +117,7 @@ module('Acceptance | host-sets | hosts', function (hooks) {
|
||||
instances.hostSet.update({ hostIds: [] });
|
||||
await visit(urls.hostSetHosts);
|
||||
assert.equal(findAll('tbody tr').length, 0);
|
||||
await click('.rose-layout-page-actions a')
|
||||
await click('.rose-layout-page-actions a:nth-child(2)')
|
||||
assert.equal(currentURL(), urls.addHosts);
|
||||
// Click three times to select, unselect, then reselect (for coverage)
|
||||
await click('tbody label');
|
||||
@@ -131,7 +134,7 @@ module('Acceptance | host-sets | hosts', function (hooks) {
|
||||
assert.equal(findAll('tbody tr').length, hostSetHostsCount);
|
||||
await click('tbody tr .rose-dropdown-button-danger');
|
||||
assert.equal(findAll('tbody tr').length, hostSetHostsCount - 1);
|
||||
await click('.rose-layout-page-actions a')
|
||||
await click('.rose-layout-page-actions a:nth-child(2)')
|
||||
assert.equal(currentURL(), urls.addHosts);
|
||||
await click('tbody label');
|
||||
await click('form [type="button"]');
|
||||
@@ -159,4 +162,74 @@ module('Acceptance | host-sets | hosts', function (hooks) {
|
||||
await click('form [type="submit"]');
|
||||
assert.ok(find('[role="alert"]'));
|
||||
});
|
||||
|
||||
test('visiting host creation from a host set', async function (assert) {
|
||||
assert.expect(1);
|
||||
await visit(urls.newHost);
|
||||
await a11yAudit();
|
||||
assert.equal(currentURL(), urls.newHost);
|
||||
});
|
||||
|
||||
test('create and add host to host set', async function (assert) {
|
||||
assert.expect(3);
|
||||
instances.hostSet.update({ hostIds: [] });
|
||||
await visit(urls.hostSet);
|
||||
assert.equal(findAll('tbody tr').length, 0);
|
||||
await click('.rose-layout-page-actions a:nth-child(1)')
|
||||
assert.equal(currentURL(), urls.newHost);
|
||||
await fillIn('[name="name"]', 'Test Name');
|
||||
await fillIn('[name="description"]', 'description');
|
||||
await click('form [type="submit"]:not(:disabled)');
|
||||
await visit(urls.hostSetHosts);
|
||||
assert.equal(findAll('tbody tr').length, 1);
|
||||
});
|
||||
|
||||
test('create and cancel host add to host set', async function (assert) {
|
||||
assert.expect(1);
|
||||
await visit(urls.newHost);
|
||||
await click('form [type="button"]');
|
||||
assert.equal(currentURL(), urls.hostSetHosts);
|
||||
});
|
||||
|
||||
test('shows error message on host creation error', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post('/hosts', () => {
|
||||
return new Response(
|
||||
400,
|
||||
{},
|
||||
{
|
||||
status: 400,
|
||||
code: 'invalid_argument',
|
||||
message: 'The request was invalid.',
|
||||
details: {},
|
||||
}
|
||||
);
|
||||
});
|
||||
instances.hostSet.update({ hostIds: [] });
|
||||
await visit(urls.newHost);
|
||||
await fillIn('[name="name"]', 'New Host');
|
||||
await click('form [type="submit"]');
|
||||
assert.ok(find('[role="alert"]'));
|
||||
});
|
||||
|
||||
test('shows error message on host addition to host set error', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.post('/host-sets/:idMethod', () => {
|
||||
return new Response(
|
||||
400,
|
||||
{},
|
||||
{
|
||||
status: 400,
|
||||
code: 'invalid_argument',
|
||||
message: 'The request was invalid.',
|
||||
details: {},
|
||||
}
|
||||
);
|
||||
});
|
||||
instances.hostSet.update({ hostIds: [] });
|
||||
await visit(urls.newHost);
|
||||
await fillIn('[name="name"]', 'New Host');
|
||||
await click('form [type="submit"]');
|
||||
assert.ok(find('[role="alert"]'));
|
||||
});
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Controller | scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/create-host', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
let controller = this.owner.lookup('controller:scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/create-host');
|
||||
assert.ok(controller);
|
||||
});
|
||||
});
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
|
||||
module('Unit | Route | scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/create-host', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
test('it exists', function(assert) {
|
||||
let route = this.owner.lookup('route:scopes/scope/projects/project/host-catalogs/host-catalog/host-sets/host-set/create-host');
|
||||
assert.ok(route);
|
||||
});
|
||||
});
|
||||
@@ -41,3 +41,5 @@ session_max_seconds:
|
||||
session_connection_limit:
|
||||
label: Maximum Connections
|
||||
help: The maximum number of connections allowed per session. For unlimited, specify "-1".
|
||||
host_set:
|
||||
label: Host Set
|
||||
@@ -67,7 +67,8 @@ host-set:
|
||||
add: Add Host Set
|
||||
create: New Host Set
|
||||
delete: Delete Host Set
|
||||
add-hosts: Add Hosts
|
||||
create-host: Create and Add Host
|
||||
add-hosts: Add Existing Host
|
||||
messages:
|
||||
welcome:
|
||||
title: Welcome to Host Sets
|
||||
@@ -75,12 +76,13 @@ host-set:
|
||||
messages:
|
||||
none:
|
||||
title: No Hosts
|
||||
description: No hosts available in this host set. Create hosts in a host catalog before assigning them to this host set.
|
||||
actions:
|
||||
new: New Host
|
||||
description: No hosts available in this host set.
|
||||
add:
|
||||
title: Add Hosts
|
||||
description: Select hosts that belong to this host set.
|
||||
add-none:
|
||||
title: No Hosts Available
|
||||
description: No hosts available for selection.
|
||||
types:
|
||||
static: Static
|
||||
host:
|
||||
|
||||
Reference in new issue
Block a user