From 516f18f73684033ac459eee67214b0e09541462c Mon Sep 17 00:00:00 2001 From: Jordan Reimer Date: Tue, 23 Nov 2021 14:17:37 -0700 Subject: [PATCH] KV automatic delete state issue in UI (#13166) * converts secret-v2-version model to native class -- fixes issues with cached values for deleted prop * adds changelog entry * adds disabled state to ToolbarLink component and disables create new version action when users cannot read metadata * updates secret-edit acceptance test --- changelog/13166.txt | 3 ++ ui/app/models/secret-v2-version.js | 31 +++++------ .../cluster/secrets/backend/secret-edit.js | 9 ++-- ui/app/styles/components/toolbar.scss | 14 ++++- .../components/secret-edit-toolbar.hbs | 2 + ui/jsconfig.json | 9 ++++ ui/lib/core/addon/components/toolbar-link.js | 10 ++-- .../templates/components/toolbar-link.hbs | 54 ++++++++++++------- ui/lib/core/stories/toolbar/toolbar-link.md | 13 +++-- .../stories/toolbar/toolbar-link.stories.js | 6 ++- .../secrets/backend/kv/secret-test.js | 9 +--- .../components/toolbar-link-test.js | 39 +++++++++++++- 12 files changed, 141 insertions(+), 58 deletions(-) create mode 100644 changelog/13166.txt create mode 100644 ui/jsconfig.json diff --git a/changelog/13166.txt b/changelog/13166.txt new file mode 100644 index 000000000..ff1ab5f89 --- /dev/null +++ b/changelog/13166.txt @@ -0,0 +1,3 @@ +```release-note:bug +ui: Fixes issue with placeholder not displaying for automatically deleted secrets when deletion time has passed +``` \ No newline at end of file diff --git a/ui/app/models/secret-v2-version.js b/ui/app/models/secret-v2-version.js index fc7ea739a..530a02535 100644 --- a/ui/app/models/secret-v2-version.js +++ b/ui/app/models/secret-v2-version.js @@ -1,20 +1,21 @@ import { belongsTo, attr } from '@ember-data/model'; -import Secret from './secret'; -import { computed } from '@ember/object'; +import SecretModel from './secret'; -export default Secret.extend({ - failedServerRead: attr('boolean'), - pathAttr: 'path', - version: attr('number'), - secret: belongsTo('secret-v2'), - path: attr('string'), - deletionTime: attr('string'), - createdTime: attr('string'), - deleted: computed('deletionTime', function() { +export default class SecretV2VersionModel extends SecretModel { + @attr('boolean') failedServerRead; + @attr('number') version; + @attr('string') path; + @attr('string') deletionTime; + @attr('string') createdTime; + @attr('boolean') detroyed; + @attr('number') currentVersion; + @belongsTo('secret-v2') secret; + + pathAttr = 'path'; + + get deleted() { const deletionTime = new Date(this.deletionTime); const now = new Date(); return deletionTime <= now; - }), - destroyed: attr('boolean'), - currentVersion: attr('number'), -}); + } +} diff --git a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js index acebf1580..03e544acd 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js +++ b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js @@ -155,12 +155,9 @@ export default Route.extend(UnloadModelRoute, { try { if (secretModel.failedServerRead) { // we couldn't read metadata, so we want to directly fetch the version - - versionModel = - this.store.peekRecord('secret-v2-version', JSON.stringify(versionId)) || - (await this.store.findRecord('secret-v2-version', JSON.stringify(versionId), { - reload: true, - })); + versionModel = await this.store.findRecord('secret-v2-version', JSON.stringify(versionId), { + reload: true, + }); } else { // we may have previously errored, so roll it back here version.rollbackAttributes(); diff --git a/ui/app/styles/components/toolbar.scss b/ui/app/styles/components/toolbar.scss index 9d94f1cde..ea7967ba1 100644 --- a/ui/app/styles/components/toolbar.scss +++ b/ui/app/styles/components/toolbar.scss @@ -84,7 +84,7 @@ color: $black; transition: background-color $speed; - &:hover { + &:hover:not(.disabled) { background-color: $ui-gray-100; border: 0; color: $blue; @@ -98,6 +98,18 @@ height: 2.5rem; padding: $size-10 $size-8; } + + &.disabled { + opacity: 0.5; + cursor: default; + + &:focus { + box-shadow: none; + } + &:hover { + background: transparent; + } + } } .toolbar-separator { diff --git a/ui/app/templates/components/secret-edit-toolbar.hbs b/ui/app/templates/components/secret-edit-toolbar.hbs index 914603903..e173977b0 100644 --- a/ui/app/templates/components/secret-edit-toolbar.hbs +++ b/ui/app/templates/components/secret-edit-toolbar.hbs @@ -101,6 +101,8 @@ @data-test-secret-edit="true" @replace={{true}} @type="add" + @disabled={{@model.failedServerRead}} + @disabledTooltip="Metadata read access is required to create new version" > Create new version diff --git a/ui/jsconfig.json b/ui/jsconfig.json new file mode 100644 index 000000000..789c3bf5f --- /dev/null +++ b/ui/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "allowJs": true + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/ui/lib/core/addon/components/toolbar-link.js b/ui/lib/core/addon/components/toolbar-link.js index 43e8baff4..48745acb3 100644 --- a/ui/lib/core/addon/components/toolbar-link.js +++ b/ui/lib/core/addon/components/toolbar-link.js @@ -7,15 +7,17 @@ * ```js * * - * + * * Create policy * * * * ``` * - * @param params=''{Array} Array to pass to LinkTo - * @param type=''{String} Use "add" to change icon + * @param {array} params - Array to pass to LinkTo + * @param {string} type - Use "add" to change icon + * @param {boolean} disabled - pass true to disable link + * @param {string} disabledTooltip - tooltip to display on hover when disabled */ import Component from '@ember/component'; @@ -27,6 +29,8 @@ export default Component.extend({ tagName: '', supportsDataTestProperties: true, type: null, + disabled: false, + disabledTooltip: null, glyph: computed('type', function() { if (this.type == 'add') { return 'plus-plain'; diff --git a/ui/lib/core/addon/templates/components/toolbar-link.hbs b/ui/lib/core/addon/templates/components/toolbar-link.hbs index 196d79cac..50d3e7995 100644 --- a/ui/lib/core/addon/templates/components/toolbar-link.hbs +++ b/ui/lib/core/addon/templates/components/toolbar-link.hbs @@ -1,20 +1,36 @@ - - {{yield}} - - + disabled=disabled + params=params + data-test-secret-edit=data-test-secret-edit + data-test-secondary-add=data-test-secondary-add + data-test-configure-link=data-test-configure-link + data-test-alias-edit-link=data-test-alias-edit-link + data-test-entity-edit-link=data-test-entity-edit-link + data-test-replication-link=data-test-replication-link + data-test-entity-merge-link=data-test-entity-merge-link + data-test-backend-view-link=data-test-backend-view-link + data-test-entity-create-link=data-test-entity-create-link + data-test-policy-create-link=data-test-policy-create-link + data-test-policy-edit-toggle=data-test-policy-edit-toggle + data-test-secret-backend-configure=data-test-secret-backend-configure +) as |LinkToComponent|}} + {{#if (and disabled disabledTooltip)}} + + + + {{yield}} + + + +
+ {{disabledTooltip}} +
+
+
+ {{else}} + + {{yield}} + + {{/if}} +{{/let}} diff --git a/ui/lib/core/stories/toolbar/toolbar-link.md b/ui/lib/core/stories/toolbar/toolbar-link.md index b3b28c5d7..66ac726ae 100644 --- a/ui/lib/core/stories/toolbar/toolbar-link.md +++ b/ui/lib/core/stories/toolbar/toolbar-link.md @@ -4,18 +4,21 @@ `ToolbarLink` components style links and buttons for the Toolbar It should only be used inside of `Toolbar`. +**Params** -| Param | Type | Default | Description | -| --- | --- | --- | --- | -| params | Array | '' | Array to pass to LinkTo | -| type | String | '' | Use "add" to change icon | +| Param | Type | Description | +| --- | --- | --- | +| params | array | Array to pass to LinkTo | +| type | string | Use "add" to change icon | +| disabled | boolean | pass true to disable link | +| disabledTooltip | string | tooltip to display on hover when disabled | **Example** ```js - + Create policy diff --git a/ui/lib/core/stories/toolbar/toolbar-link.stories.js b/ui/lib/core/stories/toolbar/toolbar-link.stories.js index f90879e76..8bc0832ec 100644 --- a/ui/lib/core/stories/toolbar/toolbar-link.stories.js +++ b/ui/lib/core/stories/toolbar/toolbar-link.stories.js @@ -1,6 +1,6 @@ import hbs from 'htmlbars-inline-precompile'; import { storiesOf } from '@storybook/ember'; -import { withKnobs, select, text } from '@storybook/addon-knobs'; +import { withKnobs, select, text, boolean } from '@storybook/addon-knobs'; import notes from './toolbar-link.md'; storiesOf('Toolbar', module) @@ -17,6 +17,8 @@ storiesOf('Toolbar', module) {{label}} @@ -27,6 +29,8 @@ storiesOf('Toolbar', module) context: { type: select('Type', ['', 'add']), label: text('Button text', 'Edit secret'), + disabled: boolean('disabled', false), + disabledTooltip: text('Tooltip to display when disabled', ''), }, }), { notes } diff --git a/ui/tests/acceptance/secrets/backend/kv/secret-test.js b/ui/tests/acceptance/secrets/backend/kv/secret-test.js index 0869ec757..14dc4252c 100644 --- a/ui/tests/acceptance/secrets/backend/kv/secret-test.js +++ b/ui/tests/acceptance/secrets/backend/kv/secret-test.js @@ -617,13 +617,8 @@ module('Acceptance | secrets/secret/create', function(hooks) { await assert .dom('[data-test-value-div="secret-key"]') .exists('secret view page and info table row with secret-key value'); - // check you can create new version - await click('[data-test-secret-edit="true"]'); - await settled(); - await fillIn('[data-test-secret-key]', 'version2'); - await editPage.save(); - await settled(); - assert.dom('[data-test-row-label="version2"]').exists('the current version displayed is the new version'); + // create new version should be disabled with no metadata read access + assert.dom('[data-test-secret-edit]').hasClass('disabled', 'Create new version action is disabled'); assert .dom('[data-test-popup-menu-trigger="version"]') .doesNotExist('the version drop down menu does not show'); diff --git a/ui/tests/integration/components/toolbar-link-test.js b/ui/tests/integration/components/toolbar-link-test.js index 4f838b460..5d7dd4a24 100644 --- a/ui/tests/integration/components/toolbar-link-test.js +++ b/ui/tests/integration/components/toolbar-link-test.js @@ -1,6 +1,6 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; +import { render, triggerEvent } from '@ember/test-helpers'; import { isPresent } from 'ember-cli-page-object'; import hbs from 'htmlbars-inline-precompile'; @@ -14,4 +14,41 @@ module('Integration | Component | toolbar-link', function(hooks) { assert.ok(isPresent('.toolbar-link')); assert.ok(isPresent('.icon')); }); + + test('it should render icons', async function(assert) { + assert.expect(2); + + await render(hbs` + + Test Link + + `); + + assert.dom('[data-test-icon="chevron-right"]').exists('Default chevron right icon renders'); + this.set('type', 'add'); + assert.dom('[data-test-icon="plus-plain"]').exists('Icon can be overriden to show plus sign'); + }); + + test('it should disable and show tooltip if provided', async function(assert) { + assert.expect(3); + + await render(hbs` + + Test Link + + `); + + assert.dom('a').hasClass('disabled', 'Link can be disabled'); + assert.dom('[data-test-popup-menu-trigger]').doesNotExist('Tooltip is hidden when not provided'); + this.set('tooltip', 'Test tooltip'); + await triggerEvent('.ember-basic-dropdown-trigger', 'mouseenter'); + assert.dom('[data-test-disabled-tooltip]').hasText(this.tooltip, 'Tooltip renders'); + }); });