Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KV 2 Toolbar delete redesign #11530

Merged
merged 34 commits into from
May 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0cd6d3f
initial setup, modify toolbar header
Monkeychip May 4, 2021
9b02bd1
footer buttons setup
Monkeychip May 5, 2021
a94fefb
setup first delete version delete method
Monkeychip May 5, 2021
5733a64
clean up
Monkeychip May 5, 2021
f613b28
handle destory all versions
Monkeychip May 5, 2021
73ef1a1
handle undelete
Monkeychip May 5, 2021
d66f584
conditional for modal and undelete
Monkeychip May 5, 2021
10f5064
remove delete from version area
Monkeychip May 5, 2021
bfdc688
modelForData in permissions
Monkeychip May 5, 2021
86099cc
setup for soft delete and modify adpater to allow DELETE in additon t…
Monkeychip May 7, 2021
772b22e
dropdown for soft delete
Monkeychip May 7, 2021
eef8d75
stuck
Monkeychip May 7, 2021
28d6617
handle all soft deletes
Monkeychip May 10, 2021
7ac7d6a
conditional for destroy all versions
Monkeychip May 11, 2021
381c9ca
remove old functionality from secret-version-menu
Monkeychip May 11, 2021
a37eadc
glimmerize secret-version-menu
Monkeychip May 11, 2021
d470b17
Updated secret version menu and version history
arnav28 May 11, 2021
e03c656
Updated icons and columns in version history
arnav28 May 11, 2021
3dbe4ba
create new component
Monkeychip May 11, 2021
307eac0
merge
Monkeychip May 11, 2021
f0cb88d
clean up
Monkeychip May 11, 2021
02f91ea
glimmerize secret delete menu
Monkeychip May 11, 2021
dd13899
fix undelete
Monkeychip May 11, 2021
012adea
Fixed radio labels in version delete menu
arnav28 May 11, 2021
62c149a
handle v1 delete
Monkeychip May 12, 2021
e568983
refining
Monkeychip May 12, 2021
471d6ec
handle errors with flash messages
Monkeychip May 12, 2021
e9e339b
add changelog
Monkeychip May 12, 2021
f605fcd
fix test
Monkeychip May 13, 2021
0c912f8
add to test
Monkeychip May 13, 2021
c365da4
amend test
Monkeychip May 13, 2021
49af0ef
address PR comments
Monkeychip May 17, 2021
1a9c91b
whoopies
Monkeychip May 17, 2021
7f20aca
add urlEncoding
Monkeychip May 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/11530.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: Redesign of KV 2 Delete toolbar.
```
29 changes: 21 additions & 8 deletions ui/app/adapters/secret-v2-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,27 @@ export default ApplicationAdapter.extend({

v2DeleteOperation(store, id, deleteType = 'delete') {
let [backend, path, version] = JSON.parse(id);

// deleteType should be 'delete', 'destroy', 'undelete'
return this.ajax(this._url(backend, path, deleteType), 'POST', { data: { versions: [version] } }).then(
() => {
let model = store.peekRecord('secret-v2-version', id);
return model && model.rollbackAttributes() && model.reload();
}
);
// deleteType should be 'delete', 'destroy', 'undelete', 'delete-latest-version', 'destroy-version'
if ((!version && deleteType === 'delete') || deleteType === 'delete-latest-version') {
Copy link
Contributor Author

@Monkeychip Monkeychip May 14, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before we were not handling the delete-latest-version which made it impossible for some people with proper permissions to execute this action. They'd get a permissions denied, because we executing the delete any version action and not the latest version deletion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also weren't handling errors, so they would silently fail.

return this.ajax(this._url(backend, path, 'data'), 'DELETE')
.then(() => {
let model = store.peekRecord('secret-v2-version', id);
return model && model.rollbackAttributes() && model.reload();
})
.catch(e => {
return e;
});
} else {
return this.ajax(this._url(backend, path, deleteType), 'POST', { data: { versions: [version] } })
.then(() => {
let model = store.peekRecord('secret-v2-version', id);
// potential that model.reload() is never called.
return model && model.rollbackAttributes() && model.reload();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw these came from the previous iteration of this, but these lines look a little strange to me, I'm not totally sure but is model.reload() ever called?

return model && model.rollbackAttributes() && model.reload();

If model is falsey (in this case undefined) just return undefined, else move on to: If the result of model.rollbackAttributes() is falsey (looking at the code rollbackAttributes always returns undefined) then return its result (undefined) else move on to: Return the result of model.reload which is the model wrapped in a promise, but if rollbackAttributes always returns undefined then we never get to this point?

As I said I'm not totally sure, and it would be weird that this method always returns undefined wrapped in a promise, maybe its not being used where ever it gets picked up?

I just ran this in web console:

({}) && (function(){})() && console.log('hi');

Which never logs 'hi'

Copy link
Contributor Author

@Monkeychip Monkeychip May 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Investigated and John is potentially right. However, in order to not cause an unknown bug, I added a comment here instead of a complete removal.

})
.catch(e => {
return e;
});
}
},

handleResponse(status, headers, payload, requestData) {
Expand Down
148 changes: 148 additions & 0 deletions ui/app/components/secret-delete-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import Ember from 'ember';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved all deletion actions into one glimmerized component.

import { inject as service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { alias } from '@ember/object/computed';
import { maybeQueryRecord } from 'vault/macros/maybe-query-record';

const getErrorMessage = errors => {
let errorMessage = errors?.join('. ') || 'Something went wrong. Check the Vault logs for more information.';
return errorMessage;
};
export default class SecretDeleteMenu extends Component {
@service store;
@service router;
@service flashMessages;

@tracked showDeleteModal = false;

@maybeQueryRecord(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL that if you have a macro (even a custom one like maybeQueryRecord, you just @'it and then put the name below it. This took me a while to figure out, so I thought it worth pointing out.

'capabilities',
context => {
if (!context.args.model) {
return;
}
let backend = context.args.model.backend;
let id = context.args.model.id;
let path = context.args.isV2
? `${encodeURIComponent(backend)}/data/${encodeURIComponent(id)}`
: `${encodeURIComponent(backend)}/${encodeURIComponent(id)}`;
return {
id: path,
};
},
'isV2',
'model',
'model.id',
'mode'
)
updatePath;
@alias('updatePath.canDelete') canDelete;
@alias('updatePath.canUpdate') canUpdate;

@maybeQueryRecord(
'capabilities',
context => {
if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return;
let [backend, id] = JSON.parse(context.args.modelForData.id);
return {
id: `${encodeURIComponent(backend)}/delete/${encodeURIComponent(id)}`,
};
},
'model.id'
)
deleteVersionPath;
@alias('deleteVersionPath.canUpdate') canDeleteAnyVersion;

@maybeQueryRecord(
'capabilities',
context => {
if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return;
let [backend, id] = JSON.parse(context.args.modelForData.id);
return {
id: `${encodeURIComponent(backend)}/undelete/${encodeURIComponent(id)}`,
};
},
'model.id'
)
undeleteVersionPath;
@alias('undeleteVersionPath.canUpdate') canUndeleteVersion;

@maybeQueryRecord(
'capabilities',
context => {
if (!context.args || !context.args.modelForData || !context.args.modelForData.id) return;
let [backend, id] = JSON.parse(context.args.modelForData.id);
return {
id: `${encodeURIComponent(backend)}/destroy/${encodeURIComponent(id)}`,
};
},
'model.id'
)
destroyVersionPath;
@alias('destroyVersionPath.canUpdate') canDestroyVersion;

@maybeQueryRecord(
'capabilities',
context => {
if (!context.args.model || !context.args.model.engine || !context.args.model.id) return;
let backend = context.args.model.engine.id;
let id = context.args.model.id;
return {
id: `${encodeURIComponent(backend)}/metadata/${encodeURIComponent(id)}`,
};
},
'model',
'model.id',
'mode'
)
v2UpdatePath;
@alias('v2UpdatePath.canDelete') canDestroyAllVersions;

get isLatestVersion() {
let { model } = this.args;
if (!model) return false;
let latestVersion = model.currentVersion;
let selectedVersion = model.selectedVersion.version;
if (latestVersion !== selectedVersion) {
return false;
}
return true;
}

@action
handleDelete(deleteType) {
Monkeychip marked this conversation as resolved.
Show resolved Hide resolved
// deleteType should be 'delete', 'destroy', 'undelete', 'delete-latest-version', 'destroy-all-versions'
if (!deleteType) {
return;
}
if (deleteType === 'destroy-all-versions' || deleteType === 'v1') {
let { id } = this.args.model;
this.args.model.destroyRecord().then(() => {
this.args.navToNearestAncestor.perform(id);
});
} else {
return this.store
.adapterFor('secret-v2-version')
.v2DeleteOperation(this.store, this.args.modelForData.id, deleteType)
.then(resp => {
if (Ember.testing) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah so I'm guessing resp is where the undefined comes in from the comment I made above for the adapter bits?

resp is only ever set when its come in via the:

catch((e) => { 
  return e
})

...in the adapter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct. We only get a resp if it's an adapter error.

return;
}
if (!resp) {
this.showDeleteModal = false;
this.args.refresh();
return;
}
if (resp.isAdapterError) {
const errorMessage = getErrorMessage(resp.errors);
this.flashMessages.danger(errorMessage);
} else {
// not likely to ever get to this situation, but adding just in case.
location.reload();
Monkeychip marked this conversation as resolved.
Show resolved Hide resolved
}
});
}
}
}
2 changes: 1 addition & 1 deletion ui/app/components/secret-edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default Component.extend(FocusOnInsertMixin, WithNavToNearestAncestor, {
'model.id',
'mode'
),
canDelete: alias('model.canDelete'),
Monkeychip marked this conversation as resolved.
Show resolved Hide resolved
canDelete: alias('updatePath.canDelete'),
canEdit: alias('updatePath.canUpdate'),

v2UpdatePath: maybeQueryRecord(
Expand Down
63 changes: 4 additions & 59 deletions ui/app/components/secret-version-menu.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,5 @@
import { maybeQueryRecord } from 'vault/macros/maybe-query-record';
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { alias, or } from '@ember/object/computed';
import Component from '@glimmer/component';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secret-version-menu used to house all the deletion actions, now it only handles version things.

export default Component.extend({
tagName: '',
store: service(),
version: null,
useDefaultTrigger: false,
onRefresh() {},

deleteVersionPath: maybeQueryRecord(
'capabilities',
context => {
let [backend, id] = JSON.parse(context.version.id);
return {
id: `${backend}/delete/${id}`,
};
},
'version.id'
),
canDeleteVersion: alias('deleteVersionPath.canUpdate'),
destroyVersionPath: maybeQueryRecord(
'capabilities',
context => {
let [backend, id] = JSON.parse(context.version.id);
return {
id: `${backend}/destroy/${id}`,
};
},
'version.id'
),
canDestroyVersion: alias('destroyVersionPath.canUpdate'),
undeleteVersionPath: maybeQueryRecord(
'capabilities',
context => {
let [backend, id] = JSON.parse(context.version.id);
return {
id: `${backend}/undelete/${id}`,
};
},
'version.id'
),
canUndeleteVersion: alias('undeleteVersionPath.canUpdate'),

isFetchingVersionCapabilities: or(
'deleteVersionPath.isPending',
'destroyVersionPath.isPending',
'undeleteVersionPath.isPending'
),
actions: {
deleteVersion(deleteType = 'destroy') {
return this.store
.adapterFor('secret-v2-version')
.v2DeleteOperation(this.store, this.version.id, deleteType)
.then(this.onRefresh);
},
},
});
export default class SecretVersionMenu extends Component {
onRefresh() {}
}
1 change: 0 additions & 1 deletion ui/app/styles/components/masked-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
height: auto;
line-height: 1rem;
min-width: $spacing-l;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the icons were showing in front of the modal, we determined it was the z-index causing this.

z-index: 100;
border: 0;
box-shadow: none;
color: $grey-light;
Expand Down
14 changes: 14 additions & 0 deletions ui/app/styles/components/modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,17 @@ pre {
background: #f7f8fa;
border-top: 1px solid #bac1cc;
}

.modal-radio-button {
display: flex;
align-items: baseline;
margin-bottom: $spacing-xs;

input {
top: 2px;
}

.helper-text {
margin-left: 10px;
}
}
Loading