UI: Allow navigate to list from View Secret card (#22502)

This commit is contained in:
Chelsea Shaw
2023-08-23 15:45:50 -05:00
committed by GitHub
parent a0217ad017
commit dd43e0db2f
6 changed files with 80 additions and 13 deletions

3
changelog/22502.txt Normal file
View File

@@ -0,0 +1,3 @@
```release-note:improvement
ui: KV View Secret card will link to list view if input ends in "/"
```

View File

@@ -22,6 +22,7 @@
* @param {string} backend - Passed to SearchSelect query method to fetch dropdown options * @param {string} backend - Passed to SearchSelect query method to fetch dropdown options
*/ */
// TODO: kv engine cleanup remove secrets related logic
import Component from '@glimmer/component'; import Component from '@glimmer/component';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { action } from '@ember/object'; import { action } from '@ember/object';
@@ -32,18 +33,38 @@ export default class GetCredentialsCard extends Component {
@tracked role = ''; @tracked role = '';
@tracked secret = ''; @tracked secret = '';
constructor() {
super(...arguments);
this.secret = this.args?.initialValue || '';
}
get buttonText() {
if (this.args.type === 'secret') {
if (this.secret.endsWith('/')) {
return 'View list';
}
return 'View secret';
}
return 'Get credentials';
}
get buttonDisabled() { get buttonDisabled() {
return !this.role && !this.secret; return !this.role && !this.secret;
} }
@action @action
transitionToCredential() { transitionToCredential(evt) {
evt.preventDefault();
const role = this.role; const role = this.role;
const secret = this.secret; const secret = this.secret;
if (role) { if (role) {
this.router.transitionTo('vault.cluster.secrets.backend.credentials', role); this.router.transitionTo('vault.cluster.secrets.backend.credentials', role);
} }
if (secret) { if (secret) {
if (secret.endsWith('/')) {
this.router.transitionTo('vault.cluster.secrets.backend.list', secret);
return;
}
this.router.transitionTo('vault.cluster.secrets.backend.show', secret); this.router.transitionTo('vault.cluster.secrets.backend.show', secret);
} }
} }

View File

@@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1 SPDX-License-Identifier: BUSL-1.1
~}} ~}}
<form class="selectable-card is-rounded no-flex data-test-get-credentials-card"> <form {{on "submit" this.transitionToCredential}} class="selectable-card is-rounded no-flex data-test-get-credentials-card">
<div class="is-flex-between is-fullwidth card-details"> <div class="is-flex-between is-fullwidth card-details">
<h3 class="title is-5">{{@title}}</h3> <h3 class="title is-5">{{@title}}</h3>
</div> </div>
@@ -16,6 +16,7 @@
@id="search-input-{{@type}}" @id="search-input-{{@type}}"
@onChange={{this.handleInput}} @onChange={{this.handleInput}}
@placeholder={{@placeholder}} @placeholder={{@placeholder}}
@initialValue={{@initialValue}}
data-test-search-roles data-test-search-roles
/> />
{{else}} {{else}}
@@ -30,13 +31,7 @@
data-test-search-roles data-test-search-roles
/> />
{{/if}} {{/if}}
<button <button type="submit" class="button is-secondary" disabled={{this.buttonDisabled}} data-test-get-credentials>
type="button" {{this.buttonText}}
class="button is-secondary"
disabled={{this.buttonDisabled}}
{{on "click" this.transitionToCredential}}
data-test-get-credentials
>
{{@title}}
</button> </button>
</form> </form>

View File

@@ -31,10 +31,10 @@
@renderInputSearch={{true}} @renderInputSearch={{true}}
@title="View secret" @title="View secret"
@searchLabel="Secret path" @searchLabel="Secret path"
@subText="Type the path of the secret you want to read" @subText="Type the path of the secret you want to view. Include a trailing slash to navigate to the list view."
@placeholder="secret/" @placeholder="secret/"
@backend="kv"
@type="secret" @type="secret"
@initialValue={{this.baseKey.id}}
/> />
</div> </div>
</div> </div>

View File

@@ -16,6 +16,12 @@ export default class inputSelect extends Component {
* *
*/ */
@tracked searchInput = ''; @tracked searchInput = '';
constructor() {
super(...arguments);
this.searchInput = this.args?.initialValue;
}
@action @action
inputChanged() { inputChanged() {
this.args.onChange(this.searchInput); this.args.onChange(this.searchInput);

View File

@@ -50,6 +50,7 @@ module('Integration | Component | get-credentials-card', function (hooks) {
test('it shows a disabled button when no item is selected', async function (assert) { test('it shows a disabled button when no item is selected', async function (assert) {
await render(hbs`<GetCredentialsCard @title={{this.title}} @searchLabel={{this.searchLabel}}/>`); await render(hbs`<GetCredentialsCard @title={{this.title}} @searchLabel={{this.searchLabel}}/>`);
assert.dom('[data-test-get-credentials]').isDisabled(); assert.dom('[data-test-get-credentials]').isDisabled();
assert.dom('[data-test-get-credentials]').hasText('Get credentials', 'Button has default text');
}); });
test('it shows button that can be clicked to credentials route when an item is selected', async function (assert) { test('it shows button that can be clicked to credentials route when an item is selected', async function (assert) {
@@ -89,6 +90,7 @@ module('Integration | Component | get-credentials-card', function (hooks) {
); );
await typeIn('[data-test-search-roles] input', 'test'); await typeIn('[data-test-search-roles] input', 'test');
assert.dom('[data-test-get-credentials]').isEnabled('submit button enables after typing input text'); assert.dom('[data-test-get-credentials]').isEnabled('submit button enables after typing input text');
assert.dom('[data-test-get-credentials]').hasText('View secret', 'Button has view secret CTA');
await click('[data-test-get-credentials]'); await click('[data-test-get-credentials]');
assert.propEqual( assert.propEqual(
this.router.transitionTo.lastCall.args, this.router.transitionTo.lastCall.args,
@@ -96,4 +98,44 @@ module('Integration | Component | get-credentials-card', function (hooks) {
'transitionTo is called with correct route and secret name' 'transitionTo is called with correct route and secret name'
); );
}); });
test('it prefills input if initialValue has value', async function (assert) {
await render(
hbs`<GetCredentialsCard @title={{this.title}} @renderInputSearch={{true}} @placeholder="secret/" @backend="kv" @type="secret" @initialValue="hello/"/>`
);
assert
.dom('[data-test-component="search-select"]')
.doesNotExist('does not render search select component');
assert.dom('[data-test-search-roles] input').hasValue('hello/', 'pre-fills search input');
assert.dom('[data-test-get-credentials]').isEnabled('submit button is enabled at render');
assert.dom('[data-test-get-credentials]').hasText('View list', 'Button has list CTA');
await typeIn('[data-test-search-roles] input', 'test');
assert
.dom('[data-test-get-credentials]')
.hasText('View secret', 'Button has view secret CTA after input');
await click('[data-test-get-credentials]');
assert.propEqual(
this.router.transitionTo.lastCall.args,
['vault.cluster.secrets.backend.show', 'hello/test'],
'transitionTo is called with correct route and secret name'
);
});
test('it goes to list route if input ends in / and type=secret', async function (assert) {
await render(
hbs`<GetCredentialsCard @title={{this.title}} @renderInputSearch={{true}} @placeholder="secret/" @backend="kv" @type="secret" />`
);
assert
.dom('[data-test-component="search-select"]')
.doesNotExist('does not render search select component');
await typeIn('[data-test-search-roles] input', 'test/');
assert.dom('[data-test-get-credentials]').hasText('View list', 'submit button has list CTA');
assert.dom('[data-test-get-credentials]').isEnabled('submit button is enabled at render');
await click('[data-test-get-credentials]');
assert.propEqual(
this.router.transitionTo.lastCall.args,
['vault.cluster.secrets.backend.list', 'test/'],
'transitionTo is called with correct route and secret name'
);
});
}); });