mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-16 03:27:01 +02:00
use model dirty tracking to track changes
This commit is contained in:
parent
e5ff92962d
commit
077f366954
@ -74,13 +74,6 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
willDestroyElement() {
|
|
||||||
this._super(...arguments);
|
|
||||||
if (this.model.isError && !this.model.isDestroyed) {
|
|
||||||
this.model.rollbackAttributes();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
waitForKeyUp: task(function*() {
|
waitForKeyUp: task(function*() {
|
||||||
while (true) {
|
while (true) {
|
||||||
let event = yield waitForEvent(document.body, 'keyup');
|
let event = yield waitForEvent(document.body, 'keyup');
|
||||||
@ -115,6 +108,25 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
canDelete: alias('updatePath.canDelete'),
|
canDelete: alias('updatePath.canDelete'),
|
||||||
canEdit: alias('updatePath.canUpdate'),
|
canEdit: alias('updatePath.canUpdate'),
|
||||||
|
|
||||||
|
v2UpdatePath: queryRecord(
|
||||||
|
'capabilities',
|
||||||
|
context => {
|
||||||
|
if (context.mode === 'create' || context.isV2 === false) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
let backend = context.model.belongsTo('engine').id;
|
||||||
|
let id = context.model.id;
|
||||||
|
return {
|
||||||
|
id: `${backend}/metadata/${id}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
'isV2',
|
||||||
|
'model',
|
||||||
|
'model.id',
|
||||||
|
'mode'
|
||||||
|
),
|
||||||
|
canEditV2Secret: alias('updatePath.canUpdate'),
|
||||||
|
|
||||||
requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'),
|
requestInFlight: or('model.isLoading', 'model.isReloading', 'model.isSaving'),
|
||||||
|
|
||||||
buttonDisabled: or(
|
buttonDisabled: or(
|
||||||
@ -164,7 +176,9 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
|
|
||||||
// successCallback is called in the context of the component
|
// successCallback is called in the context of the component
|
||||||
persistKey(successCallback) {
|
persistKey(successCallback) {
|
||||||
|
let secret = this.model;
|
||||||
let model = this.modelForData;
|
let model = this.modelForData;
|
||||||
|
let isV2 = this.isV2;
|
||||||
let key = model.get('path') || model.id;
|
let key = model.get('path') || model.id;
|
||||||
|
|
||||||
if (key.startsWith('/')) {
|
if (key.startsWith('/')) {
|
||||||
@ -174,12 +188,27 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
|
|
||||||
return model.save().then(() => {
|
return model.save().then(() => {
|
||||||
if (!model.isError) {
|
if (!model.isError) {
|
||||||
|
if (isV2 && Object.keys(secret.changedAttributes()).length) {
|
||||||
|
// save secret metadata
|
||||||
|
secret
|
||||||
|
.save()
|
||||||
|
.then(() => {
|
||||||
|
this.saveComplete(successCallback, key);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
this.set(e, e.errors.join(' '));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.saveComplete(successCallback, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
saveComplete(callback, key) {
|
||||||
if (this.wizard.featureState === 'secret') {
|
if (this.wizard.featureState === 'secret') {
|
||||||
this.wizard.transitionFeatureMachine('secret', 'CONTINUE');
|
this.wizard.transitionFeatureMachine('secret', 'CONTINUE');
|
||||||
}
|
}
|
||||||
successCallback(key);
|
callback(key);
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
checkRows() {
|
checkRows() {
|
||||||
@ -203,14 +232,11 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
|
|
||||||
handleChange() {
|
handleChange() {
|
||||||
this.set('codemirrorString', this.secretData.toJSONString(true));
|
this.set('codemirrorString', this.secretData.toJSONString(true));
|
||||||
|
this.modelForData.set('secretData', this.secretData.toJSON());
|
||||||
},
|
},
|
||||||
|
|
||||||
createOrUpdateKey(type, event) {
|
createOrUpdateKey(type, event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const newData = this.secretData.toJSON();
|
|
||||||
let model = this.modelForData;
|
|
||||||
model.set('secretData', newData);
|
|
||||||
|
|
||||||
// prevent from submitting if there's no key
|
// prevent from submitting if there's no key
|
||||||
// maybe do something fancier later
|
// maybe do something fancier later
|
||||||
if (type === 'create' && isBlank(model.get('path') || model.id)) {
|
if (type === 'create' && isBlank(model.get('path') || model.id)) {
|
||||||
@ -241,7 +267,7 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
const data = this.secretData;
|
const data = this.secretData;
|
||||||
if (isNone(data.findBy('name', ''))) {
|
if (isNone(data.findBy('name', ''))) {
|
||||||
data.pushObject({ name: '', value: '' });
|
data.pushObject({ name: '', value: '' });
|
||||||
this.set('codemirrorString', data.toJSONString(true));
|
this.send('handleChange');
|
||||||
}
|
}
|
||||||
this.checkRows();
|
this.checkRows();
|
||||||
},
|
},
|
||||||
@ -254,7 +280,7 @@ export default Component.extend(FocusOnInsertMixin, {
|
|||||||
}
|
}
|
||||||
data.removeObject(item);
|
data.removeObject(item);
|
||||||
this.checkRows();
|
this.checkRows();
|
||||||
this.set('codemirrorString', data.toJSONString(true));
|
this.send('handleChange');
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleAdvanced(bool) {
|
toggleAdvanced(bool) {
|
||||||
|
@ -12,9 +12,5 @@ export default Controller.extend(BackendCrumbMixin, {
|
|||||||
// so we have to manually bubble here
|
// so we have to manually bubble here
|
||||||
this.send('refreshModel');
|
this.send('refreshModel');
|
||||||
},
|
},
|
||||||
|
|
||||||
hasChanges(hasChanges) {
|
|
||||||
this.send('hasDataChanges', hasChanges);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,6 @@ export default Controller.extend(BackendCrumbMixin, {
|
|||||||
refresh: function() {
|
refresh: function() {
|
||||||
this.send('refreshModel');
|
this.send('refreshModel');
|
||||||
},
|
},
|
||||||
hasChanges(hasChanges) {
|
|
||||||
this.send('hasDataChanges', hasChanges);
|
|
||||||
},
|
|
||||||
toggleAdvancedEdit(bool) {
|
toggleAdvancedEdit(bool) {
|
||||||
this.set('preferAdvancedEdit', bool);
|
this.set('preferAdvancedEdit', bool);
|
||||||
this.get('backendController').set('preferAdvancedEdit', bool);
|
this.get('backendController').set('preferAdvancedEdit', bool);
|
||||||
|
@ -15,10 +15,6 @@ export default Controller.extend(BackendCrumbMixin, {
|
|||||||
this.send('refreshModel');
|
this.send('refreshModel');
|
||||||
},
|
},
|
||||||
|
|
||||||
hasChanges(hasChanges) {
|
|
||||||
this.send('hasDataChanges', hasChanges);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleAdvancedEdit(bool) {
|
toggleAdvancedEdit(bool) {
|
||||||
this.set('preferAdvancedEdit', bool);
|
this.set('preferAdvancedEdit', bool);
|
||||||
this.get('backendController').set('preferAdvancedEdit', bool);
|
this.get('backendController').set('preferAdvancedEdit', bool);
|
||||||
|
@ -17,10 +17,6 @@ export default Controller.extend(BackendCrumbMixin, {
|
|||||||
this.send('refreshModel');
|
this.send('refreshModel');
|
||||||
},
|
},
|
||||||
|
|
||||||
hasChanges(hasChanges) {
|
|
||||||
this.send('hasDataChanges', hasChanges);
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleAdvancedEdit(bool) {
|
toggleAdvancedEdit(bool) {
|
||||||
this.set('preferAdvancedEdit', bool);
|
this.set('preferAdvancedEdit', bool);
|
||||||
this.get('backendController').set('preferAdvancedEdit', bool);
|
this.get('backendController').set('preferAdvancedEdit', bool);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Secret from './secret';
|
import Secret from './secret';
|
||||||
import DS from 'ember-data';
|
import DS from 'ember-data';
|
||||||
import { bool } from '@ember/object/computed';
|
import { alias, bool } from '@ember/object/computed';
|
||||||
|
|
||||||
const { attr, belongsTo } = DS;
|
const { attr, belongsTo } = DS;
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import DS from 'ember-data';
|
import DS from 'ember-data';
|
||||||
|
import { computed } from '@ember/object';
|
||||||
import { match } from '@ember/object/computed';
|
import { match } from '@ember/object/computed';
|
||||||
|
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||||
|
|
||||||
const { attr, hasMany, belongsTo, Model } = DS;
|
const { attr, hasMany, belongsTo, Model } = DS;
|
||||||
|
|
||||||
@ -11,7 +13,18 @@ export default Model.extend({
|
|||||||
updatedTime: attr(),
|
updatedTime: attr(),
|
||||||
currentVersion: attr('number'),
|
currentVersion: attr('number'),
|
||||||
oldestVersion: attr('number'),
|
oldestVersion: attr('number'),
|
||||||
maxVersions: attr('number'),
|
maxVersions: attr('number', {
|
||||||
casRequired: attr('boolean'),
|
defaultValue: 10,
|
||||||
|
label: 'Maximum Number of Versions',
|
||||||
|
}),
|
||||||
|
casRequired: attr('boolean', {
|
||||||
|
defaultValue: false,
|
||||||
|
label: 'Require Check and Set',
|
||||||
|
helpText:
|
||||||
|
'Writes will only be allowed if the key’s current version matches the version specified in the cas parameter',
|
||||||
|
}),
|
||||||
isFolder: match('id', /\/$/),
|
isFolder: match('id', /\/$/),
|
||||||
|
fields: computed(function() {
|
||||||
|
return expandAttributeMeta(this, ['maxVersions', 'casRequired']);
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
@ -73,6 +73,7 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
return hash({
|
return hash({
|
||||||
secret: this.store.queryRecord(modelType, { id: secret, backend }).then(resp => {
|
secret: this.store.queryRecord(modelType, { id: secret, backend }).then(resp => {
|
||||||
if (modelType === 'secret-v2') {
|
if (modelType === 'secret-v2') {
|
||||||
|
let backendModel = this.modelFor('vault.cluster.secrets.backend', backend);
|
||||||
let targetVersion = parseInt(params.version || resp.currentVersion, 10);
|
let targetVersion = parseInt(params.version || resp.currentVersion, 10);
|
||||||
let version = resp.versions.findBy('version', targetVersion);
|
let version = resp.versions.findBy('version', targetVersion);
|
||||||
// 404 if there's no version
|
// 404 if there's no version
|
||||||
@ -81,6 +82,7 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
set(error, 'httpStatus', 404);
|
set(error, 'httpStatus', 404);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
resp.set('engine', backendModel);
|
||||||
|
|
||||||
return version.reload().then(() => {
|
return version.reload().then(() => {
|
||||||
resp.set('selectedVersion', version);
|
resp.set('selectedVersion', version);
|
||||||
@ -136,14 +138,17 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
},
|
},
|
||||||
|
|
||||||
willTransition(transition) {
|
willTransition(transition) {
|
||||||
if (this.get('hasChanges')) {
|
let model = this.controller.model;
|
||||||
|
let version = model.get('selectedVersion');
|
||||||
|
debugger; //eslint-disable-line
|
||||||
|
if (model.hasDirtyAttributes || (version && version.hasDirtyAttributes)) {
|
||||||
if (
|
if (
|
||||||
window.confirm(
|
window.confirm(
|
||||||
'You have unsaved changes. Navigating away will discard these changes. Are you sure you want to discard your changes?'
|
'You have unsaved changes. Navigating away will discard these changes. Are you sure you want to discard your changes?'
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
version && version.rollbackAttributes();
|
||||||
this.unloadModel();
|
this.unloadModel();
|
||||||
this.set('hasChanges', false);
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
transition.abort();
|
transition.abort();
|
||||||
@ -152,9 +157,5 @@ export default Route.extend(UnloadModelRoute, {
|
|||||||
}
|
}
|
||||||
return this._super(...arguments);
|
return this._super(...arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
hasDataChanges(hasChanges) {
|
|
||||||
this.set('hasChanges', hasChanges);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
mode=mode
|
mode=mode
|
||||||
root=backendCrumb
|
root=backendCrumb
|
||||||
capabilities=capabilities
|
capabilities=capabilities
|
||||||
onDataChange=(action "hasChanges")
|
|
||||||
onRefresh=(action "refresh")
|
onRefresh=(action "refresh")
|
||||||
onToggleAdvancedEdit=(action "toggleAdvancedEdit")
|
onToggleAdvancedEdit=(action "toggleAdvancedEdit")
|
||||||
initialKey=initialKey
|
initialKey=initialKey
|
||||||
|
Loading…
Reference in New Issue
Block a user