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
This commit is contained in:
Jordan Reimer 2021-11-23 14:17:37 -07:00 committed by GitHub
parent 201526e983
commit 516f18f736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 141 additions and 58 deletions

3
changelog/13166.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:bug
ui: Fixes issue with placeholder not displaying for automatically deleted secrets when deletion time has passed
```

View File

@ -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'),
});
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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
</ToolbarLink>

9
ui/jsconfig.json Normal file
View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"experimentalDecorators": true,
"allowJs": true
},
"exclude": [
"node_modules"
]
}

View File

@ -7,15 +7,17 @@
* ```js
* <Toolbar>
* <ToolbarActions>
* <ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add">
* <ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add" @disabled={{true}} @disabledTooltip="This link is disabled">
* Create policy
* </ToolbarLink>
* </ToolbarActions>
* </Toolbar>
* ```
*
* @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';

View File

@ -1,20 +1,36 @@
<LinkTo
{{#let (component "link-to"
class="toolbar-link"
@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}}
...attributes
>
{{yield}}
<Icon @glyph={{glyph}} />
</LinkTo>
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)}}
<ToolTip @verticalPosition="above" as |T|>
<T.trigger tabindex="-1">
<LinkToComponent ...attributes>
{{yield}} <Icon @glyph={{glyph}} data-test-icon={{glyph}} />
</LinkToComponent>
</T.trigger>
<T.content @class="tool-tip smaller-font">
<div class="box" data-test-disabled-tooltip>
{{disabledTooltip}}
</div>
</T.content>
</ToolTip>
{{else}}
<LinkToComponent ...attributes>
{{yield}} <Icon @glyph={{glyph}} data-test-icon={{glyph}} />
</LinkToComponent>
{{/if}}
{{/let}}

View File

@ -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 | <code>Array</code> | <code>&#x27;&#x27;</code> | Array to pass to LinkTo |
| type | <code>String</code> | <code>&#x27;&#x27;</code> | Use "add" to change icon |
| Param | Type | Description |
| --- | --- | --- |
| params | <code>array</code> | Array to pass to LinkTo |
| type | <code>string</code> | Use "add" to change icon |
| disabled | <code>boolean</code> | pass true to disable link |
| disabledTooltip | <code>string</code> | tooltip to display on hover when disabled |
**Example**
```js
<Toolbar>
<ToolbarActions>
<ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add">
<ToolbarLink @params={{array 'vault.cluster.policies.create'}} @type="add" @disabled={{true}} @disabledTooltip="This link is disabled">
Create policy
</ToolbarLink>
</ToolbarActions>

View File

@ -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)
<ToolbarLink
@params={{array '#'}}
@type={{type}}
@disabled={{disabled}}
@disabledTooltip={{disabledTooltip}}
>
{{label}}
</ToolbarLink>
@ -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 }

View File

@ -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');

View File

@ -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`
<ToolbarLink
@params={{array '/secrets'}}
@type={{this.type}}
>
Test Link
</ToolbarLink>
`);
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`
<ToolbarLink
@params={{array '/secrets'}}
@disabled={{true}}
@disabledTooltip={{this.tooltip}}
>
Test Link
</ToolbarLink>
`);
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');
});
});