IGNITE-8200 Web Console: Override clonedCluster in cluster-edit-form if caches or models have changed. This improves interop with "import from DB" feature, which might update caches/models of cluster currently opened for editing. The import dialog works as a separate state, so the form change detection mechanism ensures that any changes to the original cluster are safe and won't interfere with changes made by user in cluster edit form.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/77316692 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/77316692 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/77316692 Branch: refs/heads/ignite-7708 Commit: 77316692f14d083138d7515affd2d3f225b709e0 Parents: 3cebf91 Author: Ilya Borisov <klast...@gmail.com> Authored: Tue Apr 17 17:15:57 2018 +0700 Committer: Alexey Kuznetsov <akuznet...@apache.org> Committed: Tue Apr 17 17:15:57 2018 +0700 ---------------------------------------------------------------------- .../components/cluster-edit-form/controller.js | 24 +++++- .../cluster-edit-form/controller.spec.js | 81 ++++++++++++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/77316692/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.js b/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.js index 35b43e0..0207729 100644 --- a/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.js +++ b/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.js @@ -17,6 +17,7 @@ import cloneDeep from 'lodash/cloneDeep'; import get from 'lodash/get'; +import isEqual from 'lodash/isEqual'; import _ from 'lodash'; export default class ClusterEditFormController { @@ -29,9 +30,11 @@ export default class ClusterEditFormController { constructor(IgniteLegacyUtils, IgniteEventGroups, IgniteConfirm, IgniteVersion, $scope, Clusters, IgniteFormUtils) { Object.assign(this, {IgniteLegacyUtils, IgniteEventGroups, IgniteConfirm, IgniteVersion, $scope, Clusters, IgniteFormUtils}); } + $onDestroy() { this.subscription.unsubscribe(); } + $onInit() { this.available = this.IgniteVersion.available.bind(this.IgniteVersion); @@ -87,10 +90,9 @@ export default class ClusterEditFormController { this.$scope.ui = this.IgniteFormUtils.formUI(); this.$scope.ui.loadedPanels = ['checkpoint', 'serviceConfiguration', 'odbcConfiguration']; } + $onChanges(changes) { - if ( - 'cluster' in changes && get(this.clonedCluster, '_id') !== get(this.cluster, '_id') - ) { + if ('cluster' in changes && this.shouldOverwriteValue(this.cluster, this.clonedCluster)) { this.clonedCluster = cloneDeep(changes.cluster.currentValue); if (this.$scope.ui && this.$scope.ui.inputForm) { this.$scope.ui.inputForm.$setPristine(); @@ -100,14 +102,30 @@ export default class ClusterEditFormController { if ('caches' in changes) this.cachesMenu = (changes.caches.currentValue || []).map((c) => ({label: c.name, value: c._id})); } + + /** + * The form should accept incoming cluster value if: + * 1. It has different _id ("new" to real id). + * 2. Different caches or models (imported from DB). + * @param {Object} a Incoming value. + * @param {Object} b Current value. + */ + shouldOverwriteValue(a, b) { + return get(a, '_id') !== get(b, '_id') || + !isEqual(get(a, 'caches'), get(b, 'caches')) || + !isEqual(get(a, 'models'), get(b, 'models')); + } + getValuesToCompare() { return [this.cluster, this.clonedCluster].map(this.Clusters.normalize); } + save() { if (this.$scope.ui.inputForm.$invalid) return this.IgniteFormUtils.triggerValidation(this.$scope.ui.inputForm, this.$scope); this.onSave({$event: cloneDeep(this.clonedCluster)}); } + reset = () => this.clonedCluster = cloneDeep(this.cluster); confirmAndReset() { return this.IgniteConfirm.confirm('Are you sure you want to undo all changes for current cluster?') http://git-wip-us.apache.org/repos/asf/ignite/blob/77316692/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.spec.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.spec.js b/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.spec.js new file mode 100644 index 0000000..cac888f --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure-advanced/components/cluster-edit-form/controller.spec.js @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import 'mocha'; +import {assert} from 'chai'; +import {spy} from 'sinon'; +import Controller from './controller'; + +suite('cluster-edit-form controller', () => { + test('cluster binding changes', () => { + const $scope = { + ui: { + inputForm: { + $setPristine: spy(), + $setUntouched: spy() + } + } + }; + + const mocks = Controller.$inject.map((token) => { + switch (token) { + case '$scope': return $scope; + default: return null; + } + }); + + const changeBoundCluster = ($ctrl, cluster) => { + $ctrl.cluster = cluster; + $ctrl.$onChanges({ + cluster: { + currentValue: cluster + } + }); + }; + + const $ctrl = new Controller(...mocks); + + const cluster1 = {_id: 1, caches: [1, 2, 3]}; + const cluster2 = {_id: 1, caches: [1, 2, 3, 4, 5, 6], models: [1, 2, 3]}; + const cluster3 = {_id: 1, caches: [1, 2, 3, 4, 5, 6], models: [1, 2, 3], name: 'Foo'}; + + changeBoundCluster($ctrl, cluster1); + + assert.notEqual($ctrl.clonedCluster, cluster1, 'Cloned cluster is really cloned'); + assert.deepEqual($ctrl.clonedCluster, cluster1, 'Cloned cluster is really a clone of incloming value'); + assert.equal(1, $scope.ui.inputForm.$setPristine.callCount, 'Sets form pristine when cluster value changes'); + assert.equal(1, $scope.ui.inputForm.$setUntouched.callCount, 'Sets form untouched when cluster value changes'); + + changeBoundCluster($ctrl, cluster2); + + assert.deepEqual( + $ctrl.clonedCluster, + cluster2, + 'Overrides clonedCluster if incoming cluster has same id but different caches or models' + ); + assert.equal(2, $scope.ui.inputForm.$setPristine.callCount, 'Sets form pristine when bound cluster caches/models change'); + assert.equal(2, $scope.ui.inputForm.$setUntouched.callCount, 'Sets form untouched when bound cluster caches/models change'); + + changeBoundCluster($ctrl, cluster3); + + assert.deepEqual( + $ctrl.clonedCluster, + cluster2, + 'Does not change cloned cluster value if fields other than id, chaches and models change' + ); + }); +});