From 077f366954dec1b6eccb9beade69fc8b3a21968b Mon Sep 17 00:00:00 2001 From: Matthew Irish Date: Mon, 15 Oct 2018 09:38:05 -0500 Subject: [PATCH] use model dirty tracking to track changes --- ui/app/components/secret-edit.js | 58 ++++++++++++++----- .../vault/cluster/secrets/backend/actions.js | 4 -- .../vault/cluster/secrets/backend/create.js | 3 - .../vault/cluster/secrets/backend/edit.js | 4 -- .../vault/cluster/secrets/backend/show.js | 4 -- ui/app/models/secret-v2-version.js | 2 +- ui/app/models/secret-v2.js | 17 +++++- .../cluster/secrets/backend/secret-edit.js | 13 +++-- .../secrets/backend/secret-edit-layout.hbs | 1 - 9 files changed, 65 insertions(+), 41 deletions(-) diff --git a/ui/app/components/secret-edit.js b/ui/app/components/secret-edit.js index c9437f9e65..5c6c83774a 100644 --- a/ui/app/components/secret-edit.js +++ b/ui/app/components/secret-edit.js @@ -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*() { while (true) { let event = yield waitForEvent(document.body, 'keyup'); @@ -115,6 +108,25 @@ export default Component.extend(FocusOnInsertMixin, { canDelete: alias('updatePath.canDelete'), 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'), buttonDisabled: or( @@ -164,7 +176,9 @@ export default Component.extend(FocusOnInsertMixin, { // successCallback is called in the context of the component persistKey(successCallback) { + let secret = this.model; let model = this.modelForData; + let isV2 = this.isV2; let key = model.get('path') || model.id; if (key.startsWith('/')) { @@ -174,13 +188,28 @@ export default Component.extend(FocusOnInsertMixin, { return model.save().then(() => { if (!model.isError) { - if (this.wizard.featureState === 'secret') { - this.wizard.transitionFeatureMachine('secret', 'CONTINUE'); + 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); } - successCallback(key); } }); }, + saveComplete(callback, key) { + if (this.wizard.featureState === 'secret') { + this.wizard.transitionFeatureMachine('secret', 'CONTINUE'); + } + callback(key); + }, checkRows() { if (this.secretData.length === 0) { @@ -203,14 +232,11 @@ export default Component.extend(FocusOnInsertMixin, { handleChange() { this.set('codemirrorString', this.secretData.toJSONString(true)); + this.modelForData.set('secretData', this.secretData.toJSON()); }, createOrUpdateKey(type, event) { event.preventDefault(); - const newData = this.secretData.toJSON(); - let model = this.modelForData; - model.set('secretData', newData); - // prevent from submitting if there's no key // maybe do something fancier later if (type === 'create' && isBlank(model.get('path') || model.id)) { @@ -241,7 +267,7 @@ export default Component.extend(FocusOnInsertMixin, { const data = this.secretData; if (isNone(data.findBy('name', ''))) { data.pushObject({ name: '', value: '' }); - this.set('codemirrorString', data.toJSONString(true)); + this.send('handleChange'); } this.checkRows(); }, @@ -254,7 +280,7 @@ export default Component.extend(FocusOnInsertMixin, { } data.removeObject(item); this.checkRows(); - this.set('codemirrorString', data.toJSONString(true)); + this.send('handleChange'); }, toggleAdvanced(bool) { diff --git a/ui/app/controllers/vault/cluster/secrets/backend/actions.js b/ui/app/controllers/vault/cluster/secrets/backend/actions.js index 49f5834dff..17f11291f3 100644 --- a/ui/app/controllers/vault/cluster/secrets/backend/actions.js +++ b/ui/app/controllers/vault/cluster/secrets/backend/actions.js @@ -12,9 +12,5 @@ export default Controller.extend(BackendCrumbMixin, { // so we have to manually bubble here this.send('refreshModel'); }, - - hasChanges(hasChanges) { - this.send('hasDataChanges', hasChanges); - }, }, }); diff --git a/ui/app/controllers/vault/cluster/secrets/backend/create.js b/ui/app/controllers/vault/cluster/secrets/backend/create.js index e50b8e2d82..abf822816e 100644 --- a/ui/app/controllers/vault/cluster/secrets/backend/create.js +++ b/ui/app/controllers/vault/cluster/secrets/backend/create.js @@ -11,9 +11,6 @@ export default Controller.extend(BackendCrumbMixin, { refresh: function() { this.send('refreshModel'); }, - hasChanges(hasChanges) { - this.send('hasDataChanges', hasChanges); - }, toggleAdvancedEdit(bool) { this.set('preferAdvancedEdit', bool); this.get('backendController').set('preferAdvancedEdit', bool); diff --git a/ui/app/controllers/vault/cluster/secrets/backend/edit.js b/ui/app/controllers/vault/cluster/secrets/backend/edit.js index 0b63567418..b3acca8613 100644 --- a/ui/app/controllers/vault/cluster/secrets/backend/edit.js +++ b/ui/app/controllers/vault/cluster/secrets/backend/edit.js @@ -15,10 +15,6 @@ export default Controller.extend(BackendCrumbMixin, { this.send('refreshModel'); }, - hasChanges(hasChanges) { - this.send('hasDataChanges', hasChanges); - }, - toggleAdvancedEdit(bool) { this.set('preferAdvancedEdit', bool); this.get('backendController').set('preferAdvancedEdit', bool); diff --git a/ui/app/controllers/vault/cluster/secrets/backend/show.js b/ui/app/controllers/vault/cluster/secrets/backend/show.js index e9730c9d9a..26c00b359f 100644 --- a/ui/app/controllers/vault/cluster/secrets/backend/show.js +++ b/ui/app/controllers/vault/cluster/secrets/backend/show.js @@ -17,10 +17,6 @@ export default Controller.extend(BackendCrumbMixin, { this.send('refreshModel'); }, - hasChanges(hasChanges) { - this.send('hasDataChanges', hasChanges); - }, - toggleAdvancedEdit(bool) { this.set('preferAdvancedEdit', bool); this.get('backendController').set('preferAdvancedEdit', bool); diff --git a/ui/app/models/secret-v2-version.js b/ui/app/models/secret-v2-version.js index b4ddc77219..aa6c07948c 100644 --- a/ui/app/models/secret-v2-version.js +++ b/ui/app/models/secret-v2-version.js @@ -1,6 +1,6 @@ import Secret from './secret'; import DS from 'ember-data'; -import { bool } from '@ember/object/computed'; +import { alias, bool } from '@ember/object/computed'; const { attr, belongsTo } = DS; diff --git a/ui/app/models/secret-v2.js b/ui/app/models/secret-v2.js index 434c6708f2..89648e1900 100644 --- a/ui/app/models/secret-v2.js +++ b/ui/app/models/secret-v2.js @@ -1,5 +1,7 @@ import DS from 'ember-data'; +import { computed } from '@ember/object'; import { match } from '@ember/object/computed'; +import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; const { attr, hasMany, belongsTo, Model } = DS; @@ -11,7 +13,18 @@ export default Model.extend({ updatedTime: attr(), currentVersion: attr('number'), oldestVersion: attr('number'), - maxVersions: attr('number'), - casRequired: attr('boolean'), + maxVersions: attr('number', { + 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', /\/$/), + fields: computed(function() { + return expandAttributeMeta(this, ['maxVersions', 'casRequired']); + }), }); 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 c9a2fefcae..9f9aac89d5 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js +++ b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js @@ -73,6 +73,7 @@ export default Route.extend(UnloadModelRoute, { return hash({ secret: this.store.queryRecord(modelType, { id: secret, backend }).then(resp => { if (modelType === 'secret-v2') { + let backendModel = this.modelFor('vault.cluster.secrets.backend', backend); let targetVersion = parseInt(params.version || resp.currentVersion, 10); let version = resp.versions.findBy('version', targetVersion); // 404 if there's no version @@ -81,6 +82,7 @@ export default Route.extend(UnloadModelRoute, { set(error, 'httpStatus', 404); throw error; } + resp.set('engine', backendModel); return version.reload().then(() => { resp.set('selectedVersion', version); @@ -136,14 +138,17 @@ export default Route.extend(UnloadModelRoute, { }, 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 ( window.confirm( '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.set('hasChanges', false); return true; } else { transition.abort(); @@ -152,9 +157,5 @@ export default Route.extend(UnloadModelRoute, { } return this._super(...arguments); }, - - hasDataChanges(hasChanges) { - this.set('hasChanges', hasChanges); - }, }, }); diff --git a/ui/app/templates/vault/cluster/secrets/backend/secret-edit-layout.hbs b/ui/app/templates/vault/cluster/secrets/backend/secret-edit-layout.hbs index 145605ab5a..4ee2aa5ccf 100644 --- a/ui/app/templates/vault/cluster/secrets/backend/secret-edit-layout.hbs +++ b/ui/app/templates/vault/cluster/secrets/backend/secret-edit-layout.hbs @@ -5,7 +5,6 @@ mode=mode root=backendCrumb capabilities=capabilities - onDataChange=(action "hasChanges") onRefresh=(action "refresh") onToggleAdvancedEdit=(action "toggleAdvancedEdit") initialKey=initialKey