feat: create ssh type target in admin-ui (#1027)

* feat: 🎸 create ssh type target in admin-UI
This commit is contained in:
Dharini Jeeva authored and GitHub committed 2022-03-31 15:34:24 -07:00
1 parent a7bcc0c332
commit 9fae8f2a9f
7 files changed
+261 -163

No files matched your search

@@ -278,6 +278,9 @@ target:
types:
tcp: TCP
ssh: SSH
help:
tcp: This target belongs to an TCP host catalog and will use TCP to connect
ssh: This target belongs to an SSH host catalog and will use SSH to connect
host-source:
title: Host Source
title_plural: Host Sources
@@ -0,0 +1,134 @@
<Rose::Form
@onSubmit={{@submit}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
@error={{@model.errors.base}}
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@name='name'
@type='text'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
as |field|
>
{{#if @model.errors.name}}
<field.errors as |errors|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.textarea
@name='description'
@type='text'
@value={{@model.description}}
@label={{t 'form.description.label'}}
@helperText={{t 'form.description.help'}}
@error={{@model.errors.description}}
as |field|
>
{{#if @model.errors.description}}
<field.errors as |errors|>
{{#each @model.errors.description as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.fieldset>
<:title>{{t 'form.type.label'}}</:title>
<:body>
<form.radioGroup
@name='types'
@selectedValue={{@model.type}}
@changed={{@changeType}}
as |radioGroup|
>
<radioGroup.card
@label={{t (concat 'resources.target.types.' @model.type)}}
@helperText={{t (concat 'resources.target.help.' @model.type)}}
@value={{@model.type}}
@layout='wide'
readonly={{true}}
@disabled={{true}}
/>
</form.radioGroup>
</:body>
</form.fieldset>
<form.input
@name='session_max_seconds'
@type='number'
@value={{@model.session_max_seconds}}
@label={{t 'form.session_max_seconds.label'}}
@error={{@model.errors.session_max_seconds}}
@helperText={{t 'form.session_max_seconds.help'}}
min='0'
step='1'
as |field|
>
{{#if @model.errors.session_max_seconds}}
<field.errors as |errors|>
{{#each @model.errors.session_max_seconds as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.input
@name='session_connection_limit'
@type='number'
@value={{@model.session_connection_limit}}
@label={{t 'form.session_connection_limit.label'}}
@error={{@model.errors.session_connection_limit}}
@helperText={{t 'form.session_connection_limit.help'}}
min='-1'
step='1'
as |field|
>
{{#if @model.errors.session_connection_limit}}
<field.errors as |errors|>
{{#each @model.errors.session_connection_limit as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.input
@name='default_port'
@type='number'
@value={{@model.default_port}}
@label={{t 'form.default_port.label'}}
@error={{@model.errors.default_port}}
@helperText={{t 'form.default_port.help'}}
placeholder={{@defaultPort}}
min='0'
step='1'
as |field|
>
{{#if @model.errors.default_port}}
<field.errors as |errors|>
{{#each @model.errors.default_port as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
{{#if (can 'save model' @model)}}
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@enableEditText={{t 'actions.edit-form'}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
{{/if}}
</Rose::Form>
+6 -122
View File
@@ -1,122 +1,6 @@
<Rose::Form
@onSubmit={{@submit}}
@cancel={{@cancel}}
@disabled={{@model.isSaving}}
@error={{@model.errors.base}}
@showEditToggle={{if @model.isNew false true}}
as |form|
>
<form.input
@value={{@model.type}}
@label={{t 'form.type.label'}}
readonly={{true}}
@disabled={{true}}
/>
<form.input
@name='name'
@type='text'
@value={{@model.name}}
@label={{t 'form.name.label'}}
@helperText={{t 'form.name.help'}}
@error={{@model.errors.name}}
as |field|
>
{{#if @model.errors.name}}
<field.errors as |errors|>
{{#each @model.errors.name as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.textarea
@name='description'
@type='text'
@value={{@model.description}}
@label={{t 'form.description.label'}}
@helperText={{t 'form.description.help'}}
@error={{@model.errors.description}}
as |field|
>
{{#if @model.errors.description}}
<field.errors as |errors|>
{{#each @model.errors.description as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.textarea>
<form.input
@name='session_max_seconds'
@type='number'
@value={{@model.session_max_seconds}}
@label={{t 'form.session_max_seconds.label'}}
@error={{@model.errors.session_max_seconds}}
@helperText={{t 'form.session_max_seconds.help'}}
min='0'
step='1'
as |field|
>
{{#if @model.errors.session_max_seconds}}
<field.errors as |errors|>
{{#each @model.errors.session_max_seconds as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
<form.input
@name='session_connection_limit'
@type='number'
@value={{@model.session_connection_limit}}
@label={{t 'form.session_connection_limit.label'}}
@error={{@model.errors.session_connection_limit}}
@helperText={{t 'form.session_connection_limit.help'}}
min='-1'
step='1'
as |field|
>
{{#if @model.errors.session_connection_limit}}
<field.errors as |errors|>
{{#each @model.errors.session_connection_limit as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
{{! TODO this is a type-specific field }}
<form.input
@name='default_port'
@type='number'
@value={{@model.default_port}}
@label={{t 'form.default_port.label'}}
@error={{@model.errors.default_port}}
@helperText={{t 'form.default_port.help'}}
min='0'
step='1'
as |field|
>
{{! TODO need to figure out how to reference `attributes.errors` here }}
{{#if @model.errors.default_port}}
<field.errors as |errors|>
{{#each @model.errors.default_port as |error|}}
<errors.message>{{error.message}}</errors.message>
{{/each}}
</field.errors>
{{/if}}
</form.input>
{{#if (can 'save model' @model)}}
<form.actions
@disabled={{if @model.cannotSave @model.cannotSave}}
@enableEditText={{t 'actions.edit-form'}}
@submitText={{t 'actions.save'}}
@cancelText={{t 'actions.cancel'}}
/>
{{/if}}
</Rose::Form>
{{component
(concat 'form/target/' @model.type)
model=@model
submit=@submit
cancel=@cancel
}}
@@ -0,0 +1,6 @@
<Form::Target::Details
@model={{@model}}
@defaultPort='22'
@submit={{@submit}}
@cancel={{@cancel}}
/>
@@ -0,0 +1,6 @@
<Form::Target::Details
@model={{@model}}
@defaultPort='443'
@submit={{@submit}}
@cancel={{@cancel}}
/>
@@ -17,9 +17,26 @@
<page.actions>
{{#if (can 'create model' this.scope collection='targets')}}
{{#if @model}}
<Rose::LinkButton @route='scopes.scope.targets.new' @style='primary'>{{t
'titles.new'
}}</Rose::LinkButton>
<Rose::Dropdown
@text={{t 'titles.new'}}
@dropdownRight={{true}}
as |dropdown|
>
<dropdown.link
@route='scopes.scope.targets.new'
@query={{hash type='tcp'}}
>
{{t 'resources.target.types.tcp'}}
</dropdown.link>
{{#if (feature-flag 'ssh-target')}}
<dropdown.link
@route='scopes.scope.targets.new'
@query={{hash type='ssh'}}
>
{{t 'resources.target.types.ssh'}}
</dropdown.link>
{{/if}}
</Rose::Dropdown>
{{/if}}
{{/if}}
</page.actions>
@@ -37,37 +54,74 @@
</table.header>
<table.body as |body|>
{{#each @model as |target|}}
<body.row as |row|>
<row.headerCell>
{{#if (can 'read model' target)}}
<LinkTo
@route='scopes.scope.targets.target'
@model={{target.id}}
>
{{#if (feature-flag 'ssh-target')}}
<body.row as |row|>
<row.headerCell>
{{#if (can 'read model' target)}}
<LinkTo
@route='scopes.scope.targets.target'
@model={{target.id}}
>
{{target.displayName}}
</LinkTo>
{{else}}
{{target.displayName}}
</LinkTo>
{{else}}
{{target.displayName}}
{{/if}}
<p>{{target.description}}</p>
</row.headerCell>
<row.cell>
{{#if target.type}}
<Rose::Badge>
{{t (concat 'resources.target.types.' target.type)}}
</Rose::Badge>
{{/if}}
</row.cell>
<row.cell>
<Copyable
@text={{target.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{target.id}}</code>
</Copyable>
</row.cell>
</body.row>
{{/if}}
<p>{{target.description}}</p>
</row.headerCell>
<row.cell>
{{#if target.type}}
<Rose::Badge>
{{t (concat 'resources.target.types.' target.type)}}
</Rose::Badge>
{{/if}}
</row.cell>
<row.cell>
<Copyable
@text={{target.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{target.id}}</code>
</Copyable>
</row.cell>
</body.row>
{{else}}
{{#if target.isTCP}}
<body.row as |row|>
<row.headerCell>
{{#if (can 'read model' target)}}
<LinkTo
@route='scopes.scope.targets.target'
@model={{target.id}}
>
{{target.displayName}}
</LinkTo>
{{else}}
{{target.displayName}}
{{/if}}
<p>{{target.description}}</p>
</row.headerCell>
<row.cell>
{{#if target.type}}
<Rose::Badge>
{{t (concat 'resources.target.types.' target.type)}}
</Rose::Badge>
{{/if}}
</row.cell>
<row.cell>
<Copyable
@text={{target.id}}
@buttonText={{t 'actions.copy-to-clipboard'}}
@acknowledgeText={{t 'states.copied'}}
>
<code>{{target.id}}</code>
</Copyable>
</row.cell>
</body.row>
{{/if}}
{{/if}}
{{/each}}
</table.body>
</Rose::Table>
@@ -29,7 +29,8 @@ module('Acceptance | targets | create', function (hooks) {
projectScope: null,
targets: null,
target: null,
newTarget: null,
newTCPTarget: null,
newSSHTarget: null,
};
hooks.beforeEach(function () {
@@ -52,16 +53,26 @@ module('Acceptance | targets | create', function (hooks) {
urls.targets = `${urls.projectScope}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.unknownTarget = `${urls.targets}/foo`;
urls.newTarget = `${urls.targets}/new`;
urls.newTCPTarget = `${urls.targets}/new?type=tcp`;
urls.newSSHTarget = `${urls.targets}/new?type=ssh`;
// Generate resource couner
getTargetCount = () => this.server.schema.targets.all().models.length;
authenticateSession({});
});
test('can create new targets', async function (assert) {
test('can create new targets of type TCP', async function (assert) {
assert.expect(1);
const count = getTargetCount();
await visit(urls.newTarget);
await visit(urls.newTCPTarget);
await fillIn('[name="name"]', 'random string');
await click('[type="submit"]');
assert.equal(getTargetCount(), count + 1);
});
test('can create new targets of type SSH', async function (assert) {
assert.expect(1);
const count = getTargetCount();
await visit(urls.newSSHTarget);
await fillIn('[name="name"]', 'random string');
await click('[type="submit"]');
assert.equal(getTargetCount(), count + 1);
@@ -75,7 +86,7 @@ module('Acceptance | targets | create', function (hooks) {
'create'
)
);
assert.ok(find(`[href="${urls.newTarget}"]`));
assert.ok(find(`[href="${urls.newTCPTarget}"]`));
});
test('cannot navigate to new targets route without proper authorization', async function (assert) {
@@ -87,13 +98,13 @@ module('Acceptance | targets | create', function (hooks) {
'create'
)
);
assert.notOk(find(`[href="${urls.newTarget}"]`));
assert.notOk(find(`[href="${urls.newTCPTarget}"]`));
});
test('can cancel create new targets', async function (assert) {
assert.expect(2);
const count = getTargetCount();
await visit(urls.newTarget);
await visit(urls.newTCPTarget);
await fillIn('[name="name"]', 'random string');
await click('.rose-form-actions [type="button"]');
assert.equal(currentURL(), urls.targets);
@@ -121,7 +132,7 @@ module('Acceptance | targets | create', function (hooks) {
}
);
});
await visit(urls.newTarget);
await visit(urls.newTCPTarget);
await click('[type="submit"]');
assert.ok(
find('[role="alert"]').textContent.trim(),