From dd43e0db2ff352ad1e06d17c75e289ba470cbf46 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com> Date: Wed, 23 Aug 2023 15:45:50 -0500 Subject: [PATCH] UI: Allow navigate to list from View Secret card (#22502) --- changelog/22502.txt | 3 ++ ui/app/components/get-credentials-card.js | 23 +++++++++- .../components/get-credentials-card.hbs | 13 ++---- .../vault/cluster/secrets/backend/list.hbs | 6 +-- ui/lib/core/addon/components/input-search.js | 6 +++ .../components/get-credentials-card-test.js | 42 +++++++++++++++++++ 6 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 changelog/22502.txt diff --git a/changelog/22502.txt b/changelog/22502.txt new file mode 100644 index 0000000000..b9d21c2ce2 --- /dev/null +++ b/changelog/22502.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: KV View Secret card will link to list view if input ends in "/" +``` \ No newline at end of file diff --git a/ui/app/components/get-credentials-card.js b/ui/app/components/get-credentials-card.js index 999b85a07f..cd6015e9e7 100644 --- a/ui/app/components/get-credentials-card.js +++ b/ui/app/components/get-credentials-card.js @@ -22,6 +22,7 @@ * @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 { inject as service } from '@ember/service'; import { action } from '@ember/object'; @@ -32,18 +33,38 @@ export default class GetCredentialsCard extends Component { @tracked role = ''; @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() { return !this.role && !this.secret; } @action - transitionToCredential() { + transitionToCredential(evt) { + evt.preventDefault(); const role = this.role; const secret = this.secret; if (role) { this.router.transitionTo('vault.cluster.secrets.backend.credentials', role); } if (secret) { + if (secret.endsWith('/')) { + this.router.transitionTo('vault.cluster.secrets.backend.list', secret); + return; + } this.router.transitionTo('vault.cluster.secrets.backend.show', secret); } } diff --git a/ui/app/templates/components/get-credentials-card.hbs b/ui/app/templates/components/get-credentials-card.hbs index 4ded373929..93c6775d03 100644 --- a/ui/app/templates/components/get-credentials-card.hbs +++ b/ui/app/templates/components/get-credentials-card.hbs @@ -3,7 +3,7 @@ SPDX-License-Identifier: BUSL-1.1 ~}} -
+

{{@title}}

@@ -16,6 +16,7 @@ @id="search-input-{{@type}}" @onChange={{this.handleInput}} @placeholder={{@placeholder}} + @initialValue={{@initialValue}} data-test-search-roles /> {{else}} @@ -30,13 +31,7 @@ data-test-search-roles /> {{/if}} -
\ No newline at end of file diff --git a/ui/app/templates/vault/cluster/secrets/backend/list.hbs b/ui/app/templates/vault/cluster/secrets/backend/list.hbs index d8b919ad07..9ff9ff56a7 100644 --- a/ui/app/templates/vault/cluster/secrets/backend/list.hbs +++ b/ui/app/templates/vault/cluster/secrets/backend/list.hbs @@ -31,10 +31,10 @@ @renderInputSearch={{true}} @title="View secret" @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/" - @backend="kv" @type="secret" + @initialValue={{this.baseKey.id}} /> @@ -148,7 +148,7 @@ `); 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) { @@ -89,6 +90,7 @@ module('Integration | Component | get-credentials-card', function (hooks) { ); 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]').hasText('View secret', 'Button has view secret CTA'); await click('[data-test-get-credentials]'); assert.propEqual( 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' ); }); + + test('it prefills input if initialValue has value', async function (assert) { + await render( + hbs`` + ); + 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`` + ); + 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' + ); + }); });