You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2018/04/02 12:24:39 UTC
[16/24] ignite git commit: IGNITE-5466 Web Console: Configuration
reworked to cluster centric model: 1. Reworked data model. 2. Implemented
migrations. 3. Reworked UI for all screens. 4. Reworked validation. 5. Many
refactorings to improve code base
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/store/effects.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-configure/store/effects.js b/modules/web-console/frontend/app/components/page-configure/store/effects.js
new file mode 100644
index 0000000..3ab216a
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-configure/store/effects.js
@@ -0,0 +1,664 @@
+/*
+ * 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 {Observable} from 'rxjs/Observable';
+import 'rxjs/add/operator/ignoreElements';
+import 'rxjs/add/operator/let';
+import 'rxjs/add/operator/zip';
+import {merge} from 'rxjs/observable/merge';
+import {empty} from 'rxjs/observable/empty';
+import {of} from 'rxjs/observable/of';
+import {fromPromise} from 'rxjs/observable/fromPromise';
+import {uniqueName} from 'app/utils/uniqueName';
+import uniq from 'lodash/uniq';
+
+import {
+ clustersActionTypes,
+ cachesActionTypes,
+ shortClustersActionTypes,
+ shortCachesActionTypes,
+ shortModelsActionTypes,
+ shortIGFSsActionTypes,
+ modelsActionTypes,
+ igfssActionTypes
+} from './../reducer';
+
+import {
+ REMOVE_CLUSTER_ITEMS,
+ REMOVE_CLUSTER_ITEMS_CONFIRMED,
+ CONFIRM_CLUSTERS_REMOVAL,
+ CONFIRM_CLUSTERS_REMOVAL_OK,
+ COMPLETE_CONFIGURATION,
+ ADVANCED_SAVE_CLUSTER,
+ ADVANCED_SAVE_CACHE,
+ ADVANCED_SAVE_IGFS,
+ ADVANCED_SAVE_MODEL,
+ BASIC_SAVE,
+ BASIC_SAVE_AND_DOWNLOAD,
+ BASIC_SAVE_OK
+} from './actionTypes';
+
+import {
+ removeClusterItemsConfirmed,
+ advancedSaveCompleteConfiguration,
+ confirmClustersRemoval,
+ confirmClustersRemovalOK,
+ completeConfiguration,
+ basicSave,
+ basicSaveOK,
+ basicSaveErr
+} from './actionCreators';
+
+import ConfigureState from 'app/components/page-configure/services/ConfigureState';
+import ConfigurationDownload from 'app/components/page-configure/services/ConfigurationDownload';
+import ConfigSelectors from 'app/components/page-configure/store/selectors';
+import Clusters from 'app/services/Clusters';
+import Caches from 'app/services/Caches';
+import Models from 'app/services/Models';
+import IGFSs from 'app/services/IGFSs';
+import {Confirm} from 'app/services/Confirm.service';
+
+export const ofType = (type) => (s) => s.filter((a) => a.type === type);
+
+export default class ConfigEffects {
+ static $inject = [
+ ConfigureState.name,
+ Caches.name,
+ IGFSs.name,
+ Models.name,
+ ConfigSelectors.name,
+ Clusters.name,
+ '$state',
+ 'IgniteMessages',
+ 'IgniteConfirm',
+ Confirm.name,
+ ConfigurationDownload.name
+ ];
+ /**
+ * @param {ConfigureState} ConfigureState
+ * @param {Caches} Caches
+ * @param {IGFSs} IGFSs
+ * @param {Models} Models
+ * @param {ConfigSelectors} ConfigSelectors
+ * @param {Clusters} Clusters
+ * @param {object} $state
+ * @param {object} IgniteMessages
+ * @param {object} IgniteConfirm
+ * @param {Confirm} Confirm
+ * @param {ConfigurationDownload} ConfigurationDownload
+ */
+ constructor(ConfigureState, Caches, IGFSs, Models, ConfigSelectors, Clusters, $state, IgniteMessages, IgniteConfirm, Confirm, ConfigurationDownload) {
+ this.ConfigureState = ConfigureState;
+ this.ConfigSelectors = ConfigSelectors;
+ this.IGFSs = IGFSs;
+ this.Models = Models;
+ this.Caches = Caches;
+ this.Clusters = Clusters;
+ this.$state = $state;
+ this.IgniteMessages = IgniteMessages;
+ this.IgniteConfirm = IgniteConfirm;
+ this.Confirm = Confirm;
+ this.configurationDownload = ConfigurationDownload;
+
+ this.loadConfigurationEffect$ = this.ConfigureState.actions$
+ .let(ofType('LOAD_COMPLETE_CONFIGURATION'))
+ .exhaustMap((action) => {
+ return fromPromise(this.Clusters.getConfiguration(action.clusterID))
+ .switchMap(({data}) => of(
+ completeConfiguration(data),
+ {type: 'LOAD_COMPLETE_CONFIGURATION_OK', data}
+ ))
+ .catch((error) => of({
+ type: 'LOAD_COMPLETE_CONFIGURATION_ERR',
+ error: {
+ message: `Failed to load cluster configuration: ${error.data}.`
+ },
+ action
+ }));
+ });
+
+ this.storeConfigurationEffect$ = this.ConfigureState.actions$
+ .let(ofType(COMPLETE_CONFIGURATION))
+ .exhaustMap(({configuration: {cluster, caches, models, igfss}}) => of(...[
+ cluster && {type: clustersActionTypes.UPSERT, items: [cluster]},
+ caches && caches.length && {type: cachesActionTypes.UPSERT, items: caches},
+ models && models.length && {type: modelsActionTypes.UPSERT, items: models},
+ igfss && igfss.length && {type: igfssActionTypes.UPSERT, items: igfss}
+ ].filter((v) => v)));
+
+ this.saveCompleteConfigurationEffect$ = this.ConfigureState.actions$
+ .let(ofType('ADVANCED_SAVE_COMPLETE_CONFIGURATION'))
+ .switchMap((action) => {
+ const actions = [
+ {
+ type: modelsActionTypes.UPSERT,
+ items: action.changedItems.models
+ },
+ {
+ type: shortModelsActionTypes.UPSERT,
+ items: action.changedItems.models.map((m) => this.Models.toShortModel(m))
+ },
+ {
+ type: igfssActionTypes.UPSERT,
+ items: action.changedItems.igfss
+ },
+ {
+ type: shortIGFSsActionTypes.UPSERT,
+ items: action.changedItems.igfss
+ },
+ {
+ type: cachesActionTypes.UPSERT,
+ items: action.changedItems.caches
+ },
+ {
+ type: shortCachesActionTypes.UPSERT,
+ items: action.changedItems.caches.map(Caches.toShortCache)
+ },
+ {
+ type: clustersActionTypes.UPSERT,
+ items: [action.changedItems.cluster]
+ },
+ {
+ type: shortClustersActionTypes.UPSERT,
+ items: [Clusters.toShortCluster(action.changedItems.cluster)]
+ }
+ ].filter((a) => a.items.length);
+
+ return of(...actions)
+ .merge(
+ fromPromise(Clusters.saveAdvanced(action.changedItems))
+ .switchMap((res) => {
+ return of(
+ {type: 'EDIT_CLUSTER', cluster: action.changedItems.cluster},
+ {type: 'ADVANCED_SAVE_COMPLETE_CONFIGURATION_OK', changedItems: action.changedItems}
+ );
+ })
+ .catch((res) => {
+ return of({
+ type: 'ADVANCED_SAVE_COMPLETE_CONFIGURATION_ERR',
+ changedItems: action.changedItems,
+ action,
+ error: {
+ message: `Failed to save cluster "${action.changedItems.cluster.name}": ${res.data}.`
+ }
+ }, {
+ type: 'UNDO_ACTIONS',
+ actions
+ });
+ })
+ );
+ });
+
+ this.addCacheToEditEffect$ = this.ConfigureState.actions$
+ .let(ofType('ADD_CACHE_TO_EDIT'))
+ .switchMap(() => this.ConfigureState.state$.let(this.ConfigSelectors.selectCacheToEdit('new')).take(1))
+ .map((cache) => ({type: 'UPSERT_CLUSTER_ITEM', itemType: 'caches', item: cache}));
+
+ this.errorNotificationsEffect$ = this.ConfigureState.actions$
+ .filter((a) => a.error)
+ .do((action) => this.IgniteMessages.showError(action.error))
+ .ignoreElements();
+
+ this.loadUserClustersEffect$ = this.ConfigureState.actions$
+ .let(ofType('LOAD_USER_CLUSTERS'))
+ .exhaustMap((a) => {
+ return fromPromise(this.Clusters.getClustersOverview())
+ .switchMap(({data}) => of(
+ {type: shortClustersActionTypes.SET, items: data},
+ {type: `${a.type}_OK`}
+ ))
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load clusters: ${error.data}`
+ },
+ action: a
+ }));
+ });
+
+ this.loadAndEditClusterEffect$ = ConfigureState.actions$
+ .let(ofType('LOAD_AND_EDIT_CLUSTER'))
+ .exhaustMap((a) => {
+ if (a.clusterID === 'new') {
+ return of(
+ {type: 'EDIT_CLUSTER', cluster: this.Clusters.getBlankCluster()},
+ {type: 'LOAD_AND_EDIT_CLUSTER_OK'}
+ );
+ }
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectCluster(a.clusterID)).take(1)
+ .switchMap((cluster) => {
+ if (cluster) {
+ return of(
+ {type: 'EDIT_CLUSTER', cluster},
+ {type: 'LOAD_AND_EDIT_CLUSTER_OK'}
+ );
+ }
+ return fromPromise(this.Clusters.getCluster(a.clusterID))
+ .switchMap(({data}) => of(
+ {type: clustersActionTypes.UPSERT, items: [data]},
+ {type: 'EDIT_CLUSTER', cluster: data},
+ {type: 'LOAD_AND_EDIT_CLUSTER_OK'}
+ ))
+ .catch((error) => of({
+ type: 'LOAD_AND_EDIT_CLUSTER_ERR',
+ error: {
+ message: `Failed to load cluster: ${error.data}.`
+ }
+ }));
+ });
+ });
+
+ this.loadCacheEffect$ = this.ConfigureState.actions$
+ .let(ofType('LOAD_CACHE'))
+ .exhaustMap((a) => {
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectCache(a.cacheID)).take(1)
+ .switchMap((cache) => {
+ if (cache) return of({type: `${a.type}_OK`, cache});
+ return fromPromise(this.Caches.getCache(a.cacheID))
+ .switchMap(({data}) => of(
+ {type: 'CACHE', cache: data},
+ {type: `${a.type}_OK`, cache: data}
+ ));
+ })
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load cache: ${error.data}.`
+ }
+ }));
+ });
+
+ this.storeCacheEffect$ = this.ConfigureState.actions$
+ .let(ofType('CACHE'))
+ .map((a) => ({type: cachesActionTypes.UPSERT, items: [a.cache]}));
+
+ this.loadShortCachesEffect$ = ConfigureState.actions$
+ .let(ofType('LOAD_SHORT_CACHES'))
+ .exhaustMap((a) => {
+ if (!(a.ids || []).length) return of({type: `${a.type}_OK`});
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectShortCaches()).take(1)
+ .switchMap((items) => {
+ if (!items.pristine && a.ids && a.ids.every((_id) => items.value.has(_id)))
+ return of({type: `${a.type}_OK`});
+
+ return fromPromise(this.Clusters.getClusterCaches(a.clusterID))
+ .switchMap(({data}) => of(
+ {type: shortCachesActionTypes.UPSERT, items: data},
+ {type: `${a.type}_OK`}
+ ));
+ })
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load caches: ${error.data}.`
+ },
+ action: a
+ }));
+ });
+
+ this.loadIgfsEffect$ = this.ConfigureState.actions$
+ .let(ofType('LOAD_IGFS'))
+ .exhaustMap((a) => {
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectIGFS(a.igfsID)).take(1)
+ .switchMap((igfs) => {
+ if (igfs) return of({type: `${a.type}_OK`, igfs});
+ return fromPromise(this.IGFSs.getIGFS(a.igfsID))
+ .switchMap(({data}) => of(
+ {type: 'IGFS', igfs: data},
+ {type: `${a.type}_OK`, igfs: data}
+ ));
+ })
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load IGFS: ${error.data}.`
+ }
+ }));
+ });
+
+ this.storeIgfsEffect$ = this.ConfigureState.actions$
+ .let(ofType('IGFS'))
+ .map((a) => ({type: igfssActionTypes.UPSERT, items: [a.igfs]}));
+
+ this.loadShortIgfssEffect$ = ConfigureState.actions$
+ .let(ofType('LOAD_SHORT_IGFSS'))
+ .exhaustMap((a) => {
+ if (!(a.ids || []).length) {
+ return of(
+ {type: shortIGFSsActionTypes.UPSERT, items: []},
+ {type: `${a.type}_OK`}
+ );
+ }
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectShortIGFSs()).take(1)
+ .switchMap((items) => {
+ if (!items.pristine && a.ids && a.ids.every((_id) => items.value.has(_id)))
+ return of({type: `${a.type}_OK`});
+
+ return fromPromise(this.Clusters.getClusterIGFSs(a.clusterID))
+ .switchMap(({data}) => of(
+ {type: shortIGFSsActionTypes.UPSERT, items: data},
+ {type: `${a.type}_OK`}
+ ));
+ })
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load IGFSs: ${error.data}.`
+ },
+ action: a
+ }));
+ });
+
+ this.loadModelEffect$ = this.ConfigureState.actions$
+ .let(ofType('LOAD_MODEL'))
+ .exhaustMap((a) => {
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectModel(a.modelID)).take(1)
+ .switchMap((model) => {
+ if (model) return of({type: `${a.type}_OK`, model});
+ return fromPromise(this.Models.getModel(a.modelID))
+ .switchMap(({data}) => of(
+ {type: 'MODEL', model: data},
+ {type: `${a.type}_OK`, model: data}
+ ));
+ })
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load domain model: ${error.data}.`
+ }
+ }));
+ });
+
+ this.storeModelEffect$ = this.ConfigureState.actions$
+ .let(ofType('MODEL'))
+ .map((a) => ({type: modelsActionTypes.UPSERT, items: [a.model]}));
+
+ this.loadShortModelsEffect$ = this.ConfigureState.actions$
+ .let(ofType('LOAD_SHORT_MODELS'))
+ .exhaustMap((a) => {
+ if (!(a.ids || []).length) {
+ return of(
+ {type: shortModelsActionTypes.UPSERT, items: []},
+ {type: `${a.type}_OK`}
+ );
+ }
+ return this.ConfigureState.state$.let(this.ConfigSelectors.selectShortModels()).take(1)
+ .switchMap((items) => {
+ if (!items.pristine && a.ids && a.ids.every((_id) => items.value.has(_id)))
+ return of({type: `${a.type}_OK`});
+
+ return fromPromise(this.Clusters.getClusterModels(a.clusterID))
+ .switchMap(({data}) => of(
+ {type: shortModelsActionTypes.UPSERT, items: data},
+ {type: `${a.type}_OK`}
+ ));
+ })
+ .catch((error) => of({
+ type: `${a.type}_ERR`,
+ error: {
+ message: `Failed to load domain models: ${error.data}.`
+ },
+ action: a
+ }));
+ });
+
+ this.basicSaveRedirectEffect$ = this.ConfigureState.actions$
+ .let(ofType(BASIC_SAVE_OK))
+ .do((a) => this.$state.go('base.configuration.edit.basic', {clusterID: a.changedItems.cluster._id}, {location: 'replace', custom: {justIDUpdate: true}}))
+ .ignoreElements();
+
+ this.basicDownloadAfterSaveEffect$ = this.ConfigureState.actions$.let(ofType(BASIC_SAVE_AND_DOWNLOAD))
+ .zip(this.ConfigureState.actions$.let(ofType(BASIC_SAVE_OK)))
+ .pluck('1')
+ .do((a) => this.configurationDownload.downloadClusterConfiguration(a.changedItems.cluster))
+ .ignoreElements();
+
+ this.advancedSaveRedirectEffect$ = this.ConfigureState.actions$
+ .let(ofType('ADVANCED_SAVE_COMPLETE_CONFIGURATION_OK'))
+ .withLatestFrom(this.ConfigureState.actions$.let(ofType('ADVANCED_SAVE_COMPLETE_CONFIGURATION')))
+ .pluck('1', 'changedItems')
+ .map((req) => {
+ const firstChangedItem = Object.keys(req).filter((k) => k !== 'cluster')
+ .map((k) => Array.isArray(req[k]) ? [k, req[k][0]] : [k, req[k]])
+ .filter((v) => v[1])
+ .pop();
+ return firstChangedItem ? [...firstChangedItem, req.cluster] : ['cluster', req.cluster, req.cluster];
+ })
+ .do(([type, value, cluster]) => {
+ const go = (state, params = {}) => this.$state.go(
+ state, {...params, clusterID: cluster._id}, {location: 'replace', custom: {justIDUpdate: true}}
+ );
+ switch (type) {
+ case 'models': {
+ const state = 'base.configuration.edit.advanced.models.model';
+ this.IgniteMessages.showInfo(`Model "${value.valueType}" saved`);
+ if (
+ this.$state.is(state) && this.$state.params.modelID !== value._id
+ ) return go(state, {modelID: value._id});
+ break;
+ }
+ case 'caches': {
+ const state = 'base.configuration.edit.advanced.caches.cache';
+ this.IgniteMessages.showInfo(`Cache "${value.name}" saved`);
+ if (
+ this.$state.is(state) && this.$state.params.cacheID !== value._id
+ ) return go(state, {cacheID: value._id});
+ break;
+ }
+ case 'igfss': {
+ const state = 'base.configuration.edit.advanced.igfs.igfs';
+ this.IgniteMessages.showInfo(`IGFS "${value.name}" saved`);
+ if (
+ this.$state.is(state) && this.$state.params.igfsID !== value._id
+ ) return go(state, {igfsID: value._id});
+ break;
+ }
+ case 'cluster': {
+ const state = 'base.configuration.edit.advanced.cluster';
+ this.IgniteMessages.showInfo(`Cluster "${value.name}" saved`);
+ if (
+ this.$state.is(state) && this.$state.params.clusterID !== value._id
+ ) return go(state);
+ break;
+ }
+ default: break;
+ }
+ })
+ .ignoreElements();
+
+ this.removeClusterItemsEffect$ = this.ConfigureState.actions$
+ .let(ofType(REMOVE_CLUSTER_ITEMS))
+ .exhaustMap((a) => {
+ return a.confirm
+ // TODO: list items to remove in confirmation
+ ? fromPromise(this.Confirm.confirm('Are you sure want to remove these items?'))
+ .mapTo(a)
+ .catch(() => empty())
+ : of(a);
+ })
+ .map((a) => removeClusterItemsConfirmed(a.clusterID, a.itemType, a.itemIDs));
+
+ this.persistRemovedClusterItemsEffect$ = this.ConfigureState.actions$
+ .let(ofType(REMOVE_CLUSTER_ITEMS_CONFIRMED))
+ .withLatestFrom(this.ConfigureState.actions$.let(ofType(REMOVE_CLUSTER_ITEMS)))
+ .filter(([a, b]) => {
+ return a.itemType === b.itemType
+ && b.save
+ && JSON.stringify(a.itemIDs) === JSON.stringify(b.itemIDs);
+ })
+ .pluck('0')
+ .withLatestFrom(this.ConfigureState.state$.pluck('edit'))
+ .map(([action, edit]) => advancedSaveCompleteConfiguration(edit));
+
+ this.confirmClustersRemovalEffect$ = this.ConfigureState.actions$
+ .let(ofType(CONFIRM_CLUSTERS_REMOVAL))
+ .pluck('clusterIDs')
+ .switchMap((ids) => this.ConfigureState.state$.let(this.ConfigSelectors.selectClusterNames(ids)).take(1))
+ .exhaustMap((names) => {
+ return fromPromise(this.Confirm.confirm(`
+ <p>Are you sure want to remove these clusters?</p>
+ <ul>${names.map((name) => `<li>${name}</li>`).join('')}</ul>
+ `))
+ .map(confirmClustersRemovalOK)
+ .catch(() => Observable.empty());
+ });
+
+ this.persistRemovedClustersLocallyEffect$ = this.ConfigureState.actions$
+ .let(ofType(CONFIRM_CLUSTERS_REMOVAL_OK))
+ .withLatestFrom(this.ConfigureState.actions$.let(ofType(CONFIRM_CLUSTERS_REMOVAL)))
+ .switchMap(([, {clusterIDs}]) => of(
+ {type: shortClustersActionTypes.REMOVE, ids: clusterIDs},
+ {type: clustersActionTypes.REMOVE, ids: clusterIDs}
+ ));
+
+ this.persistRemovedClustersRemotelyEffect$ = this.ConfigureState.actions$
+ .let(ofType(CONFIRM_CLUSTERS_REMOVAL_OK))
+ .withLatestFrom(
+ this.ConfigureState.actions$.let(ofType(CONFIRM_CLUSTERS_REMOVAL)),
+ this.ConfigureState.actions$.let(ofType(shortClustersActionTypes.REMOVE)),
+ this.ConfigureState.actions$.let(ofType(clustersActionTypes.REMOVE))
+ )
+ .switchMap(([, {clusterIDs}, ...backup]) => this.Clusters.removeCluster$(clusterIDs)
+ .mapTo({
+ type: 'REMOVE_CLUSTERS_OK'
+ })
+ .catch((e) => of(
+ {
+ type: 'REMOVE_CLUSTERS_ERR',
+ error: {
+ message: `Failed to remove clusters: ${e.data}`
+ }
+ },
+ {
+ type: 'UNDO_ACTIONS',
+ actions: backup
+ }
+ ))
+ );
+
+ this.notifyRemoteClustersRemoveSuccessEffect$ = this.ConfigureState.actions$
+ .let(ofType('REMOVE_CLUSTERS_OK'))
+ .withLatestFrom(this.ConfigureState.actions$.let(ofType(CONFIRM_CLUSTERS_REMOVAL)))
+ .do(([, {clusterIDs}]) => this.IgniteMessages.showInfo(`Cluster(s) removed: ${clusterIDs.length}`))
+ .ignoreElements();
+
+ const _applyChangedIDs = (edit, {cache, igfs, model, cluster} = {}) => ({
+ cluster: {
+ ...edit.changes.cluster,
+ ...(cluster ? cluster : {}),
+ caches: cache ? uniq([...edit.changes.caches.ids, cache._id]) : edit.changes.caches.ids,
+ igfss: igfs ? uniq([...edit.changes.igfss.ids, igfs._id]) : edit.changes.igfss.ids,
+ models: model ? uniq([...edit.changes.models.ids, model._id]) : edit.changes.models.ids
+ },
+ caches: cache ? uniq([...edit.changes.caches.changedItems, cache]) : edit.changes.caches.changedItems,
+ igfss: igfs ? uniq([...edit.changes.igfss.changedItems, igfs]) : edit.changes.igfss.changedItems,
+ models: model ? uniq([...edit.changes.models.changedItems, model]) : edit.changes.models.changedItems
+ });
+
+ this.advancedSaveCacheEffect$ = merge(
+ this.ConfigureState.actions$.let(ofType(ADVANCED_SAVE_CLUSTER)),
+ this.ConfigureState.actions$.let(ofType(ADVANCED_SAVE_CACHE)),
+ this.ConfigureState.actions$.let(ofType(ADVANCED_SAVE_IGFS)),
+ this.ConfigureState.actions$.let(ofType(ADVANCED_SAVE_MODEL)),
+ )
+ .withLatestFrom(this.ConfigureState.state$.pluck('edit'))
+ .map(([action, edit]) => ({
+ type: 'ADVANCED_SAVE_COMPLETE_CONFIGURATION',
+ changedItems: _applyChangedIDs(edit, action)
+ }));
+
+ this.basicSaveEffect$ = this.ConfigureState.actions$
+ .let(ofType(BASIC_SAVE))
+ .merge(this.ConfigureState.actions$.let(ofType(BASIC_SAVE_AND_DOWNLOAD)))
+ .withLatestFrom(this.ConfigureState.state$.pluck('edit'))
+ .switchMap(([action, edit]) => {
+ const changedItems = _applyChangedIDs(edit, {cluster: action.cluster});
+ const actions = [{
+ type: cachesActionTypes.UPSERT,
+ items: changedItems.caches
+ },
+ {
+ type: shortCachesActionTypes.UPSERT,
+ items: changedItems.caches
+ },
+ {
+ type: clustersActionTypes.UPSERT,
+ items: [changedItems.cluster]
+ },
+ {
+ type: shortClustersActionTypes.UPSERT,
+ items: [this.Clusters.toShortCluster(changedItems.cluster)]
+ }
+ ].filter((a) => a.items.length);
+
+ return Observable.of(...actions)
+ .merge(
+ Observable.fromPromise(this.Clusters.saveBasic(changedItems))
+ .switchMap((res) => Observable.of(
+ {type: 'EDIT_CLUSTER', cluster: changedItems.cluster},
+ basicSaveOK(changedItems)
+ ))
+ .catch((res) => Observable.of(
+ basicSaveErr(changedItems, res),
+ {type: 'UNDO_ACTIONS', actions}
+ ))
+ );
+ });
+
+ this.basicSaveOKMessagesEffect$ = this.ConfigureState.actions$
+ .let(ofType(BASIC_SAVE_OK))
+ .do((action) => this.IgniteMessages.showInfo(`Cluster "${action.changedItems.cluster.name}" saved.`))
+ .ignoreElements();
+ }
+
+ /**
+ * @name etp
+ * @function
+ * @param {object} action
+ * @returns {Promise}
+ */
+ /**
+ * @name etp^2
+ * @function
+ * @param {string} type
+ * @param {object} [params]
+ * @returns {Promise}
+ */
+ etp = (...args) => {
+ const action = typeof args[0] === 'object' ? args[0] : {type: args[0], ...args[1]};
+ const ok = `${action.type}_OK`;
+ const err = `${action.type}_ERR`;
+
+ setTimeout(() => this.ConfigureState.dispatchAction(action));
+ return this.ConfigureState.actions$
+ .filter((a) => a.type === ok || a.type === err)
+ .take(1)
+ .map((a) => {
+ if (a.type === err)
+ throw a;
+ else
+ return a;
+ })
+ .toPromise();
+ };
+
+ connect() {
+ return merge(
+ ...Object.keys(this).filter((k) => k.endsWith('Effect$')).map((k) => this[k])
+ ).do((a) => this.ConfigureState.dispatchAction(a)).subscribe();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/store/selectors.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-configure/store/selectors.js b/modules/web-console/frontend/app/components/page-configure/store/selectors.js
new file mode 100644
index 0000000..0f60214
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-configure/store/selectors.js
@@ -0,0 +1,170 @@
+/*
+ * 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 {uniqueName} from 'app/utils/uniqueName';
+import {of} from 'rxjs/observable/of';
+import {empty} from 'rxjs/observable/empty';
+import {combineLatest} from 'rxjs/observable/combineLatest';
+import 'rxjs/add/operator/mergeMap';
+import 'rxjs/add/observable/combineLatest';
+import {Observable} from 'rxjs/Observable';
+import {defaultNames} from '../defaultNames';
+
+import {default as Caches} from 'app/services/Caches';
+import {default as Clusters} from 'app/services/Clusters';
+import {default as IGFSs} from 'app/services/IGFSs';
+import {default as Models} from 'app/services/Models';
+
+const isDefined = (s) => s.filter((v) => v);
+const selectItems = (path) => (s) => s.filter((s) => s).pluck(path).filter((v) => v);
+const selectValues = (s) => s.map((v) => v && [...v.value.values()]);
+export const selectMapItem = (mapPath, key) => (s) => s.pluck(mapPath).map((v) => v && v.get(key));
+const selectMapItems = (mapPath, keys) => (s) => s.pluck(mapPath).map((v) => v && keys.map((key) => v.get(key)));
+const selectItemToEdit = ({items, itemFactory, defaultName = '', itemID}) => (s) => s.switchMap((item) => {
+ if (item) return of(Object.assign(itemFactory(), item));
+ if (itemID === 'new') return items.take(1).map((items) => Object.assign(itemFactory(), {name: uniqueName(defaultName, items)}));
+ if (!itemID) return of(null);
+ return empty();
+});
+const currentShortItems = ({changesKey, shortKey}) => (state$) => {
+ return Observable.combineLatest(
+ state$.pluck('edit', 'changes', changesKey).let(isDefined).distinctUntilChanged(),
+ state$.pluck(shortKey, 'value').let(isDefined).distinctUntilChanged()
+ )
+ .map(([{ids = [], changedItems}, shortItems]) => {
+ if (!ids.length || !shortItems) return [];
+ return ids.map((id) => changedItems.find(({_id}) => _id === id) || shortItems.get(id));
+ })
+ .map((v) => v.filter((v) => v));
+};
+
+const selectNames = (itemIDs, nameAt = 'name') => (items) => items
+ .pluck('value')
+ .map((items) => itemIDs.map((id) => items.get(id)[nameAt]));
+
+export default class ConfigSelectors {
+ static $inject = ['Caches', 'Clusters', 'IGFSs', 'Models'];
+ /**
+ * @param {Caches} Caches
+ * @param {Clusters} Clusters
+ * @param {IGFSs} IGFSs
+ * @param {Models} Models
+ */
+ constructor(Caches, Clusters, IGFSs, Models) {
+ this.Caches = Caches;
+ this.Clusters = Clusters;
+ this.IGFSs = IGFSs;
+ this.Models = Models;
+
+ /**
+ * @param {string} id
+ * @returns {(state$: Observable) => Observable<ig.config.model.DomainModel>}
+ */
+ this.selectModel = (id) => selectMapItem('models', id);
+ /**
+ * @returns {(state$: Observable) => Observable<{pristine: boolean, value: Map<string, ig.config.model.ShortDomainModel>}>}
+ */
+ this.selectShortModels = () => selectItems('shortModels');
+ this.selectShortModelsValue = () => (state$) => state$.let(this.selectShortModels()).let(selectValues);
+ /**
+ * @returns {(state$: Observable) => Observable<Array<ig.config.cluster.ShortCluster>>}
+ */
+ this.selectShortClustersValue = () => (state$) => state$.let(this.selectShortClusters()).let(selectValues);
+ /**
+ * @returns {(state$: Observable) => Observable<Array<string>>}
+ */
+ this.selectClusterNames = (clusterIDs) => (state$) => state$
+ .let(this.selectShortClusters())
+ .let(selectNames(clusterIDs));
+ }
+ selectCluster = (id) => selectMapItem('clusters', id);
+ selectShortClusters = () => selectItems('shortClusters');
+ selectCache = (id) => selectMapItem('caches', id);
+ selectIGFS = (id) => selectMapItem('igfss', id);
+ selectShortCaches = () => selectItems('shortCaches');
+ selectShortCachesValue = () => (state$) => state$.let(this.selectShortCaches()).let(selectValues);
+ selectShortIGFSs = () => selectItems('shortIgfss');
+ selectShortIGFSsValue = () => (state$) => state$.let(this.selectShortIGFSs()).let(selectValues);
+ selectShortModelsValue = () => (state$) => state$.let(this.selectShortModels()).let(selectValues);
+ selectCacheToEdit = (cacheID) => (state$) => state$
+ .let(this.selectCache(cacheID))
+ .distinctUntilChanged()
+ .let(selectItemToEdit({
+ items: state$.let(this.selectCurrentShortCaches),
+ itemFactory: () => this.Caches.getBlankCache(),
+ defaultName: defaultNames.cache,
+ itemID: cacheID
+ }));
+ selectIGFSToEdit = (itemID) => (state$) => state$
+ .let(this.selectIGFS(itemID))
+ .distinctUntilChanged()
+ .let(selectItemToEdit({
+ items: state$.let(this.selectCurrentShortIGFSs),
+ itemFactory: () => this.IGFSs.getBlankIGFS(),
+ defaultName: defaultNames.igfs,
+ itemID
+ }));
+ selectModelToEdit = (itemID) => (state$) => state$
+ .let(this.selectModel(itemID))
+ .distinctUntilChanged()
+ .let(selectItemToEdit({
+ items: state$.let(this.selectCurrentShortModels),
+ itemFactory: () => this.Models.getBlankModel(),
+ itemID
+ }));
+ selectClusterToEdit = (clusterID, defaultName = defaultNames.cluster) => (state$) => state$
+ .let(this.selectCluster(clusterID))
+ .distinctUntilChanged()
+ .let(selectItemToEdit({
+ items: state$.let(this.selectShortClustersValue()),
+ itemFactory: () => this.Clusters.getBlankCluster(),
+ defaultName,
+ itemID: clusterID
+ }));
+ selectCurrentShortCaches = currentShortItems({changesKey: 'caches', shortKey: 'shortCaches'});
+ selectCurrentShortIGFSs = currentShortItems({changesKey: 'igfss', shortKey: 'shortIgfss'});
+ selectCurrentShortModels = currentShortItems({changesKey: 'models', shortKey: 'shortModels'});
+ selectClusterShortCaches = (clusterID) => (state$) => {
+ if (clusterID === 'new') return of([]);
+ return combineLatest(
+ state$.let(this.selectCluster(clusterID)).pluck('caches'),
+ state$.let(this.selectShortCaches()).pluck('value'),
+ (ids, items) => ids.map((id) => items.get(id))
+ );
+ };
+ selectCompleteClusterConfiguration = ({clusterID, isDemo}) => (state$) => {
+ const hasValues = (array) => !array.some((v) => !v);
+ return state$.let(this.selectCluster(clusterID))
+ .exhaustMap((cluster) => {
+ if (!cluster) return of({__isComplete: false});
+ const withSpace = (array) => array.map((c) => ({...c, space: cluster.space}));
+ return Observable.forkJoin(
+ state$.let(selectMapItems('caches', cluster.caches || [])).take(1),
+ state$.let(selectMapItems('models', cluster.models || [])).take(1),
+ state$.let(selectMapItems('igfss', cluster.igfss || [])).take(1),
+ )
+ .map(([caches, models, igfss]) => ({
+ cluster,
+ caches,
+ domains: models,
+ igfss,
+ spaces: [{_id: cluster.space, demo: isDemo}],
+ __isComplete: !!cluster && !(!hasValues(caches) || !hasValues(models) || !hasValues(igfss))
+ }));
+ });
+ };
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/style.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-configure/style.scss b/modules/web-console/frontend/app/components/page-configure/style.scss
index 424e6ba..94a3ebc 100644
--- a/modules/web-console/frontend/app/components/page-configure/style.scss
+++ b/modules/web-console/frontend/app/components/page-configure/style.scss
@@ -16,8 +16,289 @@
*/
page-configure {
- h1 version-picker {
+ font-family: Roboto;
+ flex: 1 0 auto;
+ display: flex;
+ flex-direction: column;
+
+ &>.pc-page-header {
+ display: flex;
+
+ .pc-page-header-title {
+ margin-right: auto;
+ }
+ }
+
+ .pc-form-actions-panel {
+ display: flex;
+ flex-direction: row;
+ padding: 10px 20px 10px 30px;
+ box-shadow: 0 0px 4px 0 rgba(0, 0, 0, 0.2), 0px 3px 4px -1px rgba(0, 0, 0, 0.2);
+ position: sticky;
+ bottom: 0px;
+ // margin: 20px -30px -30px;
+ background: white;
+ border-radius: 0 0 4px 4px;
+ z-index: 10;
+
+ &>*+* {
+ margin-left: 10px;
+ }
+
+ .link-primary + .link-primary {
+ margin-left: 40px;
+ }
+
+ .pc-form-actions-panel__right-after {
+ width: 0;
+ margin-left: auto;
+ }
+ }
+
+ .pc-hide-tooltips {
+ .tipField:not(.fa-remove), .icon-help, [ignite-icon='info'] {
+ display: none;
+ }
+ }
+
+ .pc-content-container {
+ position: relative;
+
+ &, &>ui-view {
+ flex: 1 0 auto;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .pc-tooltips-toggle {
+ position: absolute;
+ top: 0;
+ right: 0;
+ }
+
+ &>.tabs {
+ flex: 0 0 auto;
+ }
+ }
+
+ .pc-tooltips-toggle {
+ display: inline-flex;
+ height: 40px;
+ align-items: center;
+ width: auto;
+ max-width: none !important;
+ user-select: none;
+
+ &>*:not(input) {
+ margin-left: 5px;
+ flex: 0 0 auto;
+ }
+
+ input {
+ pointer-events: none;
+ }
+
+ &>div {
+ margin-left: 10px !important;
+ }
+ }
+}
+
+.pc-form-group {
+ $input-height: 36px;
+ width: 100%;
+ border: 1px solid rgba(197, 197, 197, 0.5);
+ border-radius: 4px;
+ padding-bottom: 10px;
+ padding-top: $input-height / 2;
+ margin-top: $input-height / -2;
+
+ &:empty {
+ display: none;
+ }
+
+}
+
+.pc-form-group__text-title {
+ transform: translateY(-9px);
+ --pc-form-group-title-bg-color: white;
+
+ &>span {
+ padding-left: 10px;
+ padding-right: 10px;
+ background: var(--pc-form-group-title-bg-color);
+ }
+
+ &>.form-field-checkbox .ignite-form-field__control {
+ & > span {
+ position: relative;
+
+ &:after {
+ content: '';
+ display: block;
+ position: absolute;
+ background-color: var(--pc-form-group-title-bg-color);
+ z-index: -1;
+ top: 0;
+ bottom: 0;
+ left: -26px;
+ right: -5px;
+ }
+ }
+ [ignite-icon] {
+ background-color: var(--pc-form-group-title-bg-color);
+ }
+ }
+
+ &+.pc-form-group {
+ padding-top: 10px;
+ }
+}
+
+.pc-form-grid-row > .pc-form-group__text-title[class*='pc-form-grid-col-'] {
+ margin-top: 20px !important;
+}
+
+list-editable .pc-form-group__text-title {
+ --pc-form-group-title-bg-color: var(--le-row-bg-color);
+}
+
+.pc-form-grid-row {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-content: flex-start;
+
+ &>[class*='pc-form-grid-col-'] {
+ margin: 10px 0 0 !important;
+ max-width: none;
+ padding: 0 10px;
+ flex-grow: 0;
+ flex-shrink: 0;
+ }
+
+ .group-section {
+ width: 100%;
+ }
+
+ &>.pc-form-grid__break {
+ flex: 1 0 100%;
+ }
+}
+
+.pc-form-grid-row {
+ &>.pc-form-grid-col-10 {
+ flex-basis: calc(100% / 6);
+ }
+
+ &>.pc-form-grid-col-20 {
+ flex-basis: calc(100% / 3);
+ }
+
+ &>.pc-form-grid-col-30 {
+ flex-basis: calc(100% / 2);
+ }
+
+ &>.pc-form-grid-col-40 {
+ flex-basis: calc(100% / 1.5);
+ }
+
+ &>.pc-form-grid-col-60 {
+ flex-basis: calc(100% / 1);
+ }
+
+ @media(max-width: 992px) {
+ &>.pc-form-grid-col-10 {
+ flex-basis: calc(25%);
+ }
+ &>.pc-form-grid-col-20 {
+ flex-basis: calc(50%);
+ }
+
+ &>.pc-form-grid-col-30 {
+ flex-basis: calc(100%);
+ }
+
+ &>.pc-form-grid-col-60 {
+ flex-basis: calc(100%);
+ }
+ }
+ // IE11 does not count padding towards flex width
+ @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
+ &>.pc-form-grid-col-10 {
+ flex-basis: calc(99.9% / 6 - 20px);
+ }
+
+ &>.pc-form-grid-col-20 {
+ flex-basis: calc(99.9% / 3 - 20px);
+ }
+
+ &>.pc-form-grid-col-30 {
+ flex-basis: calc(100% / 2 - 20px);
+ }
+
+ &>.pc-form-grid-col-40 {
+ flex-basis: calc(100% / 1.5 - 20px);
+ }
+
+ &>.pc-form-grid-col-60 {
+ flex-basis: calc(100% / 1 - 20px);
+ }
+
+ @media(max-width: 992px) {
+ &>.pc-form-grid-col-20 {
+ flex-basis: calc(50% - 20px);
+ }
+
+ &>.pc-form-grid-col-30 {
+ flex-basis: calc(100% - 20px);
+ }
+
+ &>.pc-form-grid-col-60 {
+ flex-basis: calc(100% - 20px);
+ }
+ }
+ }
+}
+.pc-page-header {
+ font-family: Roboto;
+ font-size: 24px;
+ line-height: 36px;
+ color: #393939;
+ margin: 40px 0 30px;
+ padding: 0;
+ border: none;
+ word-break: break-all;
+
+ .pc-page-header-sub {
+ font-size: 14px;
+ line-height: 20px;
+ color: #757575;
+ margin-left: 8px;
+ }
+
+ version-picker {
margin-left: 8px;
- vertical-align: bottom;
+ vertical-align: 2px;
+ }
+
+ button-import-models {
+ flex: 0 0 auto;
+ margin-left: 30px;
+ position: relative;
+ // For some reason button is heigher up by 1px than on overview page
+ top: 1px;
}
+}
+
+.pc-form-grid__text-only-item {
+ padding-top: 16px;
+ align-self: flex-start;
+ height: 54px;
+ display: flex;
+ justify-content: center;
+ align-content: center;
+ align-items: center;
+ background: white;
+ z-index: 2;
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/template.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-configure/template.pug b/modules/web-console/frontend/app/components/page-configure/template.pug
index 72ca614..c56320b 100644
--- a/modules/web-console/frontend/app/components/page-configure/template.pug
+++ b/modules/web-console/frontend/app/components/page-configure/template.pug
@@ -14,17 +14,34 @@
See the License for the specific language governing permissions and
limitations under the License.
-.docs-content
- header
- h1
- | Configure
- version-picker
- div
- ul.tabs.tabs--blue
- li(role='presentation' ui-sref-active='active')
- a(ui-sref='base.configuration.tabs.basic') Basic
- li(role='presentation' ui-sref-active='active')
- a(ui-sref='base.configuration.tabs.advanced') Advanced
+h1.pc-page-header
+ span.pc-page-header-title
+ | {{ $ctrl.clusterName$|async:this }}
+ version-picker
+ button-import-models(cluster-id='$ctrl.clusterID$|async:this')
+div.pc-content-container
+ ul.tabs.tabs--blue
+ li(role='presentation' ui-sref-active='active')
+ a(ui-sref='base.configuration.edit.basic') Basic
+ li(role='presentation' ui-sref-active='{active: "base.configuration.edit.advanced"}')
+ a(ui-sref='base.configuration.edit.advanced.cluster') Advanced
- .panel--ignite
- ui-view
\ No newline at end of file
+ label.pc-tooltips-toggle.switcher--ignite
+ svg.icon-left(
+ ignite-icon='info'
+ bs-tooltip=''
+ data-title='This setting is needed to hide and show tooltips with hints.'
+ data-placement='left'
+ )
+ span Tooltips
+ input(type='checkbox' ng-model='$ctrl.tooltipsVisible')
+ div
+
+ ui-view.theme--ignite(
+ ignite-loading='configuration'
+ ignite-loading-text='{{ $ctrl.loadingText }}'
+ ignite-loading-position='top'
+ ng-class=`{
+ 'pc-hide-tooltips': !$ctrl.tooltipsVisible
+ }`
+ )
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/transitionHooks/errorState.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-configure/transitionHooks/errorState.js b/modules/web-console/frontend/app/components/page-configure/transitionHooks/errorState.js
new file mode 100644
index 0000000..f36a931
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-configure/transitionHooks/errorState.js
@@ -0,0 +1,55 @@
+/*
+ * 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 {RejectType} from '@uirouter/angularjs';
+
+const isPromise = (object) => object && typeof object.then === 'function';
+const match = {
+ to(state) {
+ return state.data && state.data.errorState;
+ }
+};
+const go = ($transition) => $transition.router.stateService.go(
+ $transition.to().data.errorState,
+ $transition.params(),
+ {location: 'replace'}
+);
+
+/**
+ * @returns {Array<Promise>}
+ */
+const getResolvePromises = ($transition) => $transition.getResolveTokens()
+ .filter((token) => typeof token === 'string')
+ .map((token) => $transition.injector().getAsync(token))
+ .filter(isPromise);
+
+/**
+ * Global transition hook that redirects to data.errorState if:
+ * 1. Transition throws an error.
+ * 2. Any resolve promise throws an error. onError does not work for this case if resolvePolicy is set to 'NOWAIT'.
+ */
+export const errorState = ($uiRouter) => {
+ $uiRouter.transitionService.onError(match, ($transition) => {
+ if ($transition.error().type !== RejectType.ERROR) return;
+ go($transition);
+ });
+ $uiRouter.transitionService.onStart(match, ($transition) => {
+ Promise.all(getResolvePromises($transition)).catch((e) => go($transition));
+ });
+};
+
+errorState.$inject = ['$uiRouter'];
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/types/uirouter.d.ts
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-configure/types/uirouter.d.ts b/modules/web-console/frontend/app/components/page-configure/types/uirouter.d.ts
new file mode 100644
index 0000000..90d8434
--- /dev/null
+++ b/modules/web-console/frontend/app/components/page-configure/types/uirouter.d.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 * as _uirouter from '@uirouter/angularjs'
+export as namespace uirouter
+export = _uirouter
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-profile/style.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-profile/style.scss b/modules/web-console/frontend/app/components/page-profile/style.scss
index a96914e..f4eae1a 100644
--- a/modules/web-console/frontend/app/components/page-profile/style.scss
+++ b/modules/web-console/frontend/app/components/page-profile/style.scss
@@ -28,4 +28,8 @@ page-profile {
.btn-ignite + .btn-ignite {
margin-left: 10px;
}
+
+ [ignite-icon='expand'], [ignite-icon='collapse'] {
+ color: #757575;
+ }
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
index 55bee85..fcb3609 100644
--- a/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
+++ b/modules/web-console/frontend/app/components/page-queries/components/queries-notebook/controller.js
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+import {nonEmpty, nonNil} from 'app/utils/lodashMixins';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/merge';
import 'rxjs/add/operator/switchMap';
@@ -147,8 +147,8 @@ class Paragraph {
let cause = err;
- while (_.nonNil(cause)) {
- if (_.nonEmpty(cause.className) &&
+ while (nonNil(cause)) {
+ if (nonEmpty(cause.className) &&
_.includes(['SQLException', 'JdbcSQLException', 'QueryCancelledException'], JavaTypes.shortClassName(cause.className))) {
this.error.message = cause.message || cause.className;
@@ -158,10 +158,10 @@ class Paragraph {
cause = cause.cause;
}
- if (_.isEmpty(this.error.message) && _.nonEmpty(err.className)) {
+ if (_.isEmpty(this.error.message) && nonEmpty(err.className)) {
this.error.message = 'Internal cluster error';
- if (_.nonEmpty(err.className))
+ if (nonEmpty(err.className))
this.error.message += ': ' + err.className;
}
};
@@ -171,7 +171,7 @@ class Paragraph {
if (_.isNil(this.queryArgs))
return null;
- if (_.nonEmpty(this.error.message))
+ if (nonEmpty(this.error.message))
return 'error';
if (_.isEmpty(this.rows))
@@ -197,7 +197,7 @@ class Paragraph {
}
queryExecuted() {
- return _.nonEmpty(this.meta) || _.nonEmpty(this.error.message);
+ return nonEmpty(this.meta) || nonEmpty(this.error.message);
}
scanExplain() {
@@ -209,11 +209,11 @@ class Paragraph {
}
chartColumnsConfigured() {
- return _.nonEmpty(this.chartKeyCols) && _.nonEmpty(this.chartValCols);
+ return nonEmpty(this.chartKeyCols) && nonEmpty(this.chartValCols);
}
chartTimeLineEnabled() {
- return _.nonEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE);
+ return nonEmpty(this.chartKeyCols) && _.eq(this.chartKeyCols[0], TIME_LINE);
}
executionInProgress(showLocal = false) {
@@ -922,7 +922,7 @@ export class NotebookCtrl {
});
// Await for demo caches.
- if (!$ctrl.demoStarted && $root.IgniteDemoMode && _.nonEmpty(cacheNames)) {
+ if (!$ctrl.demoStarted && $root.IgniteDemoMode && nonEmpty(cacheNames)) {
$ctrl.demoStarted = true;
Loading.finish('sqlLoading');
@@ -935,7 +935,7 @@ export class NotebookCtrl {
const _startWatch = () => {
const awaitClusters$ = fromPromise(
- agentMgr.startClusterWatch('Back to Configuration', 'base.configuration.tabs.advanced.clusters'));
+ agentMgr.startClusterWatch('Back to Configuration', 'base.configuration.overview'));
const finishLoading$ = defer(() => {
if (!$root.IgniteDemoMode)
@@ -1919,7 +1919,7 @@ export class NotebookCtrl {
const tab = ' ';
const addToTrace = (item) => {
- if (_.nonNil(item)) {
+ if (nonNil(item)) {
const clsName = _.isEmpty(item.className) ? '' : '[' + JavaTypes.shortClassName(item.className) + '] ';
scope.content.push((scope.content.length > 0 ? tab : '') + clsName + (item.message || ''));
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/controller.js b/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/controller.js
index 8131483..7c06f2a 100644
--- a/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/controller.js
+++ b/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/controller.js
@@ -126,7 +126,7 @@ export class NotebooksListCtrl {
_checkActionsAllow() {
// Dissallow clone and rename if more then one item is selectted.
- const oneItemIsSelected = this.gridApi.selection.getSelectedRows().length === 1;
+ const oneItemIsSelected = this.gridApi.selection.legacyGetSelectedRows().length === 1;
this.actionOptions[0].available = oneItemIsSelected;
this.actionOptions[1].available = oneItemIsSelected;
}
@@ -154,7 +154,7 @@ export class NotebooksListCtrl {
async renameNotebok() {
try {
- const currentNotebook = this.gridApi.selection.getSelectedRows()[0];
+ const currentNotebook = this.gridApi.selection.legacyGetSelectedRows()[0];
const newNotebookName = await this.IgniteInput.input('Rename notebook', 'Notebook name', currentNotebook.name);
if (this.getNotebooksNames().find((name) => newNotebookName === name))
@@ -174,7 +174,7 @@ export class NotebooksListCtrl {
async cloneNotebook() {
try {
- const clonedNotebook = Object.assign({}, this.gridApi.selection.getSelectedRows()[0]);
+ const clonedNotebook = Object.assign({}, this.gridApi.selection.legacyGetSelectedRows()[0]);
const newNotebookName = await this.IgniteInput.clone(clonedNotebook.name, this.getNotebooksNames());
this.IgniteLoading.start('notebooksLoading');
@@ -201,7 +201,7 @@ export class NotebooksListCtrl {
async deleteNotebooks() {
try {
this.IgniteLoading.start('notebooksLoading');
- await this.IgniteNotebook.removeBatch(this.gridApi.selection.getSelectedRows());
+ await this.IgniteNotebook.removeBatch(this.gridApi.selection.legacyGetSelectedRows());
await this.IgniteLoading.finish('notebooksLoading');
this._loadAllNotebooks();
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/template.tpl.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/template.tpl.pug b/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/template.tpl.pug
index 25785a4..75b5e99 100644
--- a/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/template.tpl.pug
+++ b/modules/web-console/frontend/app/components/page-queries/components/queries-notebooks-list/template.tpl.pug
@@ -34,7 +34,7 @@ page-queries-slot(slot-name="'queriesButtons'" ng-if="!$root.IgniteDemoMode")
label: 'Actions',
model: '$ctrl.action',
name: 'action',
- disabled: '$ctrl.gridApi.selection.getSelectedRows().length === 0',
+ disabled: '$ctrl.gridApi.selection.legacyGetSelectedRows().length === 0',
required: false,
options: '$ctrl.actionOptions'
})
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/version-picker/style.scss
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/version-picker/style.scss b/modules/web-console/frontend/app/components/version-picker/style.scss
index 98a4e9d..6d962c2 100644
--- a/modules/web-console/frontend/app/components/version-picker/style.scss
+++ b/modules/web-console/frontend/app/components/version-picker/style.scss
@@ -29,9 +29,8 @@ version-picker {
padding-bottom: 1px;
}
- .icon-help {
+ [ignite-icon] {
margin-left: 5px;
- font-size: 16px;
}
.dropdown-menu a {
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/version-picker/template.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/components/version-picker/template.pug b/modules/web-console/frontend/app/components/version-picker/template.pug
index 1abe471..5d35b78 100644
--- a/modules/web-console/frontend/app/components/version-picker/template.pug
+++ b/modules/web-console/frontend/app/components/version-picker/template.pug
@@ -24,10 +24,14 @@
)
| {{$ctrl.currentVersion}}
span.icon-right.fa.fa-caret-down
-
-i.icon-help(
+
+svg.icon-help(
+ ignite-icon='info'
bs-tooltip=''
- data-title='Web Console supports multiple Ignite versions.<br /> \
- Select version you need to configure cluster.'
+ data-title=`
+ Web Console supports multiple Ignite versions.
+ <br>
+ Select version you need to configure cluster.
+ `
data-placement='right'
-)
+)
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/core/activities/Activities.data.d.ts
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/core/activities/Activities.data.d.ts b/modules/web-console/frontend/app/core/activities/Activities.data.d.ts
new file mode 100644
index 0000000..88f9dd4
--- /dev/null
+++ b/modules/web-console/frontend/app/core/activities/Activities.data.d.ts
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+interface IActivityDataResponse {
+ action: string,
+ amount: number,
+ date: string,
+ group: string,
+ owner: string,
+ _id: string
+}
+
+/**
+ * Activities data service
+ */
+declare class ActivitiesData {
+ /**
+ * Posts activity to backend, sends current state if no options specified
+ */
+ post({group, action}?:{group?: string, action?: string}): ng.IPromise<ng.IHttpResponse<IActivityDataResponse>>
+}
+
+export default ActivitiesData
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/core/activities/Activities.data.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/core/activities/Activities.data.js b/modules/web-console/frontend/app/core/activities/Activities.data.js
index 8d9447c..35b44e6 100644
--- a/modules/web-console/frontend/app/core/activities/Activities.data.js
+++ b/modules/web-console/frontend/app/core/activities/Activities.data.js
@@ -18,6 +18,10 @@
export default class ActivitiesData {
static $inject = ['$http', '$state'];
+ /**
+ * @param {ng.IHttpService} $http
+ * @param {uirouter.StateService} $state
+ */
constructor($http, $state) {
this.$http = $http;
this.$state = $state;
@@ -26,8 +30,10 @@ export default class ActivitiesData {
post(options = {}) {
let { group, action } = options;
- action = action || this.$state.$current.url.source;
- group = group || action.match(/^\/([^/]+)/)[1];
+ // TODO IGNITE-5466: since upgrade to UIRouter 1, "url.source" is undefined.
+ // Actions like that won't be saved to DB. Think of a better solution later.
+ action = action || this.$state.$current.url.source || '';
+ group = group || (action.match(/^\/([^/]+)/) || [])[1];
return this.$http.post('/api/v1/activities/page', { group, action });
}
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/data/getting-started.json
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/data/getting-started.json b/modules/web-console/frontend/app/data/getting-started.json
index fd1869c..caed920 100644
--- a/modules/web-console/frontend/app/data/getting-started.json
+++ b/modules/web-console/frontend/app/data/getting-started.json
@@ -74,23 +74,6 @@
]
},
{
- "title": "Summary",
- "message": [
- "<div class='col-xs-7'>",
- " <img src='/images/summary.png' width='100%' />",
- "</div>",
- "<div class='col-xs-5'>",
- " <ul>",
- " <li>Preview XML configuration</li>",
- " <li>Preview code configuration</li>",
- " <li>Preview Docker file</li>",
- " <li>Preview POM dependencies</li>",
- " <li>Download ready-to-use project</li>",
- " </ul>",
- "</div>"
- ]
- },
- {
"title": "SQL Queries",
"message": [
"<div class='col-xs-7'>",
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/data/i18n.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/data/i18n.js b/modules/web-console/frontend/app/data/i18n.js
index 3385f60..d992153 100644
--- a/modules/web-console/frontend/app/data/i18n.js
+++ b/modules/web-console/frontend/app/data/i18n.js
@@ -18,11 +18,19 @@
export default {
'/agent/start': 'Agent start',
'/agent/download': 'Agent download',
- '/configuration/clusters': 'Configure clusters',
- '/configuration/caches': 'Configure caches',
- '/configuration/domains': 'Configure domain model',
- '/configuration/igfs': 'Configure IGFS',
- '/configuration/summary': 'Configurations summary',
+ 'base.configuration.overview': 'Cluster configurations',
+ '/configuration/overview': 'Cluster configurations',
+ 'base.configuration.edit.basic': 'Basic cluster configuration edit',
+ '/configuration/new': 'Сluster configuration create',
+ '/configuration/new/basic': 'Basic cluster configuration create',
+ '/configuration/new/advanced/cluster': 'Advanced cluster configuration create',
+ 'base.configuration.edit.advanced.cluster': 'Advanced cluster configuration edit',
+ 'base.configuration.edit.advanced.caches': 'Advanced cluster caches',
+ 'base.configuration.edit.advanced.caches.cache': 'Advanced cluster cache edit',
+ 'base.configuration.edit.advanced.models': 'Advanced cluster models',
+ 'base.configuration.edit.advanced.models.model': 'Advanced cluster model edit',
+ 'base.configuration.edit.advanced.igfs': 'Advanced cluster IGFSs',
+ 'base.configuration.edit.advanced.igfs.igfs': 'Advanced cluster IGFS edit',
'/configuration/download': 'Download project',
'/demo/resume': 'Demo resume',
'/demo/reset': 'Demo reset',
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/directives/on-focus-out.directive.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/directives/on-focus-out.directive.js b/modules/web-console/frontend/app/directives/on-focus-out.directive.js
index 802691f..fe59477 100644
--- a/modules/web-console/frontend/app/directives/on-focus-out.directive.js
+++ b/modules/web-console/frontend/app/directives/on-focus-out.directive.js
@@ -15,23 +15,93 @@
* limitations under the License.
*/
-export default ['$parse', ($parse) => {
- return ($scope, $element, $attrs) => {
- const parsedExpr = $parse($attrs.igniteOnFocusOut);
+/**
+ * @type {ng.IComponentController}
+ */
+class OnFocusOutController {
+ /** @type {OnFocusOutController} */
+ parent;
+ /** @type {Array<OnFocusOutController>} */
+ children = [];
+ /** @type {Array<string>} */
+ ignoredClasses = [];
+ /** @type {function} */
+ igniteOnFocusOut;
- const handlerCheckFocusOut = (FocusClick) => {
- if ($element.find(FocusClick.target).length)
- return;
+ static $inject = ['$element', '$window', '$scope'];
+ /**
+ * @param {JQLite} $element
+ * @param {ng.IWindowService} $window
+ * @param {ng.IScope} $scope
+ */
+ constructor($element, $window, $scope) {
+ this.$element = $element;
+ this.$window = $window;
+ this.$scope = $scope;
- $scope.$evalAsync(() => parsedExpr($scope));
+ /** @param {MouseEvent|FocusEvent} e */
+ this._eventHandler = (e) => {
+ this.children.forEach((c) => c._eventHandler(e));
+ if (this.shouldPropagate(e) && this.isFocused) {
+ this.$scope.$applyAsync(() => {
+ this.igniteOnFocusOut();
+ this.isFocused = false;
+ });
+ }
};
+ /** @param {FocusEvent} e */
+ this._onFocus = (e) => {
+ this.isFocused = true;
+ };
+ }
+ $onDestroy() {
+ this.$window.removeEventListener('click', this._eventHandler, true);
+ this.$window.removeEventListener('focusin', this._eventHandler, true);
+ this.$element[0].removeEventListener('focus', this._onFocus, true);
+ if (this.parent) this.parent.children.splice(this.parent.children.indexOf(this), 1);
+ this.$element = this.$window = this._eventHandler = this._onFocus = null;
+ }
+ shouldPropagate(e) {
+ return !this.targetHasIgnoredClasses(e) && this.targetIsOutOfElement(e);
+ }
+ targetIsOutOfElement(e) {
+ return !this.$element.find(e.target).length;
+ }
+ targetHasIgnoredClasses(e) {
+ return this.ignoredClasses.some((c) => e.target.classList.contains(c));
+ }
+ /**
+ * @param {ng.IOnChangesObject} changes [description]
+ */
+ $onChanges(changes) {
+ if (
+ 'ignoredClasses' in changes &&
+ changes.ignoredClasses.currentValue !== changes.ignoredClasses.previousValue
+ )
+ this.ignoredClasses = changes.ignoredClasses.currentValue.split(' ').concat('body-overlap');
+ }
+ $onInit() {
+ if (this.parent) this.parent.children.push(this);
+ }
+ $postLink() {
+ this.$window.addEventListener('click', this._eventHandler, true);
+ this.$window.addEventListener('focusin', this._eventHandler, true);
+ this.$element[0].addEventListener('focus', this._onFocus, true);
+ }
+}
- window.addEventListener('click', handlerCheckFocusOut, true);
- window.addEventListener('focusin', handlerCheckFocusOut, true);
-
- $scope.$on('$destroy', () => {
- window.removeEventListener('click', handlerCheckFocusOut, true);
- window.removeEventListener('focusin', handlerCheckFocusOut, true);
- });
+/**
+ * @type {ng.IDirectiveFactory}
+ */
+export default function() {
+ return {
+ controller: OnFocusOutController,
+ require: {
+ parent: '^^?igniteOnFocusOut'
+ },
+ bindToController: {
+ igniteOnFocusOut: '&',
+ ignoredClasses: '@?igniteOnFocusOutIgnoredClasses'
+ }
};
-}];
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/directives/ui-ace-pojos/ui-ace-pojos.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/directives/ui-ace-pojos/ui-ace-pojos.controller.js b/modules/web-console/frontend/app/directives/ui-ace-pojos/ui-ace-pojos.controller.js
index 774d73e..3f9580f 100644
--- a/modules/web-console/frontend/app/directives/ui-ace-pojos/ui-ace-pojos.controller.js
+++ b/modules/web-console/frontend/app/directives/ui-ace-pojos/ui-ace-pojos.controller.js
@@ -15,6 +15,8 @@
* limitations under the License.
*/
+import {nonNil} from 'app/utils/lodashMixins';
+
export default ['$scope', 'JavaTypes', 'JavaTransformer', function($scope, JavaTypes, generator) {
const ctrl = this;
@@ -47,7 +49,7 @@ export default ['$scope', 'JavaTypes', 'JavaTransformer', function($scope, JavaT
const classes = ctrl.classes = [];
_.forEach(ctrl.pojos, (pojo) => {
- if (_.nonNil(pojo.keyClass))
+ if (nonNil(pojo.keyClass))
classes.push(pojo.keyType);
classes.push(pojo.valueType);
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/directives/ui-ace.controller.js
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/directives/ui-ace.controller.js b/modules/web-console/frontend/app/directives/ui-ace.controller.js
index 96e9c5e..3b35b2a 100644
--- a/modules/web-console/frontend/app/directives/ui-ace.controller.js
+++ b/modules/web-console/frontend/app/directives/ui-ace.controller.js
@@ -34,23 +34,6 @@ export default class IgniteUiAceGeneratorFactory {
this.generate = (cluster) => this.generatorFactory.cluster(cluster, this.Version.currentSbj.getValue(), this.client === 'true');
break;
- case 'clusterCaches':
- this.generate = (cluster, caches) => {
- const clusterCaches = _.reduce(caches, (acc, cache) => {
- if (_.includes(cluster.caches, cache.value))
- acc.push(cache.cache);
-
- return acc;
- }, []);
-
- const cfg = this.generatorFactory.generator.clusterGeneral(cluster, available);
-
- this.generatorFactory.generator.clusterCaches(cluster, clusterCaches, null, available, false, cfg);
-
- return this.generatorFactory.toSection(cfg);
- };
-
- break;
case 'cacheStore':
case 'cacheQuery':
this.generate = (cache, domains) => {
@@ -79,27 +62,13 @@ export default class IgniteUiAceGeneratorFactory {
break;
case 'clusterServiceConfiguration':
this.generate = (cluster, caches) => {
- const clusterCaches = _.reduce(caches, (acc, cache) => {
- if (_.includes(cluster.caches, cache.value))
- acc.push(cache.cache);
-
- return acc;
- }, []);
-
- return this.generatorFactory.clusterServiceConfiguration(cluster.serviceConfigurations, clusterCaches);
+ return this.generatorFactory.clusterServiceConfiguration(cluster.serviceConfigurations, caches);
};
break;
case 'clusterCheckpoint':
this.generate = (cluster, caches) => {
- const clusterCaches = _.reduce(caches, (acc, cache) => {
- if (_.includes(cluster.caches, cache.value))
- acc.push(cache.cache);
-
- return acc;
- }, []);
-
- return this.generatorFactory.clusterCheckpoint(cluster, available, clusterCaches);
+ return this.generatorFactory.clusterCheckpoint(cluster, available, caches);
};
break;
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/helpers/jade/form.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/helpers/jade/form.pug b/modules/web-console/frontend/app/helpers/jade/form.pug
index 6fddbf6..44eaed9 100644
--- a/modules/web-console/frontend/app/helpers/jade/form.pug
+++ b/modules/web-console/frontend/app/helpers/jade/form.pug
@@ -24,5 +24,3 @@ include ./form/form-field-checkbox
include ./form/form-field-number
include ./form/form-field-up
include ./form/form-field-down
-
-include ./form/form-group
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/helpers/jade/form/form-field-checkbox.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/helpers/jade/form/form-field-checkbox.pug b/modules/web-console/frontend/app/helpers/jade/form/form-field-checkbox.pug
index fcd6f9d..a8236a9 100644
--- a/modules/web-console/frontend/app/helpers/jade/form/form-field-checkbox.pug
+++ b/modules/web-console/frontend/app/helpers/jade/form/form-field-checkbox.pug
@@ -15,25 +15,30 @@
limitations under the License.
mixin form-field-checkbox(label, model, name, disabled, required, tip)
- .checkbox(id=`{{ ${name} }}Field`)
- label(id=`{{ ${name} }}Label`)
- .input-tip
+ label.form-field-checkbox.ignite-form-field
+ .ignite-form-field__control
+ input(
+ id=`{{ ${name} }}Input`
+ name=`{{ ${name} }}`
+ type='checkbox'
+
+ ng-model=model
+ ng-required=required && `${required}`
+ ng-disabled=disabled && `${disabled}`
+ expose-ignite-form-field-control='$input'
+ )&attributes(attributes ? attributes.attributes ? attributes.attributes : attributes : {})
+ span #{label}
+ +tooltip(tip, tipOpts, 'tipLabel')
+ .ignite-form-field__errors(
+ ng-messages=`$input.$error`
+ ng-show=`($input.$dirty || $input.$touched || $input.$submitted) && $input.$invalid`
+ )
if block
block
- else
- input(
- id=`{{ ${name} }}Input`
- name=`{{ ${name} }}`
- type='checkbox'
-
- data-ng-model=model
- data-ng-required=required && `${required}`
- data-ng-disabled=disabled && `${disabled}`
-
- data-ng-focus='tableReset()'
-
- data-ignite-form-panel-field=''
- )
- span #{label}
+ if required
+ +form-field-feedback(name, 'required', `${errLbl} could not be empty!`)
- +tooltip(tip, tipOpts, 'tipLabel')
+mixin sane-form-field-checkbox({label, model, name, disabled, required, tip})
+ +form-field-checkbox(label, model, name, disabled = false, required = false, tip)&attributes(attributes)
+ if block
+ block
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/helpers/jade/form/form-field-datalist.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/helpers/jade/form/form-field-datalist.pug b/modules/web-console/frontend/app/helpers/jade/form/form-field-datalist.pug
index 6da1255..888634b 100644
--- a/modules/web-console/frontend/app/helpers/jade/form/form-field-datalist.pug
+++ b/modules/web-console/frontend/app/helpers/jade/form/form-field-datalist.pug
@@ -23,29 +23,30 @@ mixin form-field-datalist(label, model, name, disabled, required, placeholder, o
name=`{{ ${name} }}`
placeholder=placeholder
- data-ng-model=model
+ ng-model=model
- data-ng-required=required && `${required}`
- data-ng-disabled=disabled && `${disabled}` || `!${options}.length`
+ ng-required=required && `${required}`
+ ng-disabled=disabled && `${disabled}` || `!${options}.length`
bs-typeahead
bs-options=`item for item in ${options}`
container='body'
data-min-length='1'
ignite-retain-selection
-
- data-ignite-form-panel-field=''
+ expose-ignite-form-field-control='$input'
)&attributes(attributes.attributes)
.ignite-form-field
- +ignite-form-field__label(label, name, required)
- .ignite-form-field__control
+ +ignite-form-field__label(label, name, required, disabled)
+tooltip(tip, tipOpts)
-
- +form-field-feedback(name, 'required', errLbl + ' could not be empty!')
-
+ .ignite-form-field__control
+ .input-tip
+ +form-field-input(attributes=attributes)
+ .ignite-form-field__errors(
+ ng-messages=`$input.$error`
+ ng-if=`($input.$dirty || $input.$touched || $input.$submitted) && $input.$invalid`
+ )
if block
block
- .input-tip
- +form-field-input(attributes=attributes)
+ +form-field-feedback(name, 'required', `${errLbl} could not be empty!`)
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/helpers/jade/form/form-field-dropdown.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/helpers/jade/form/form-field-dropdown.pug b/modules/web-console/frontend/app/helpers/jade/form/form-field-dropdown.pug
index cf7d50a..c6579e3 100644
--- a/modules/web-console/frontend/app/helpers/jade/form/form-field-dropdown.pug
+++ b/modules/web-console/frontend/app/helpers/jade/form/form-field-dropdown.pug
@@ -16,33 +16,45 @@
mixin ignite-form-field-dropdown(label, model, name, disabled, required, multiple, placeholder, placeholderEmpty, options, tip)
mixin form-field-input()
+ -var errLbl = label.substring(0, label.length - 1)
+
button.select-toggle.form-control(
+ type='button'
id=`{{ ${name} }}Input`
name=`{{ ${name} }}`
data-placeholder=placeholderEmpty ? `{{ ${options}.length > 0 ? '${placeholder}' : '${placeholderEmpty}' }}` : placeholder
- data-ng-model=model
- data-ng-disabled=disabled && `${disabled}`
- data-ng-required=required && `${required}`
+ ng-model=model
+ ng-disabled=disabled && `${disabled}`
+ ng-required=required && `${required}`
bs-select
bs-options=`item.value as item.label for item in ${options}`
+ expose-ignite-form-field-control='$input'
data-multiple=multiple ? '1' : false
tabindex='0'
-
- data-ignite-form-panel-field=''
)&attributes(attributes.attributes)
- .ignite-form-field
- +ignite-form-field__label(label, name, required)
- .ignite-form-field__control
+ .ignite-form-field.ignite-form-field-dropdown
+ +ignite-form-field__label(label, name, required, disabled)
+tooltip(tip, tipOpts)
-
+ .ignite-form-field__control
+ .input-tip
+ +form-field-input(attributes=attributes)
+ .ignite-form-field__errors(
+ ng-messages=`$input.$error`
+ ng-show=`($input.$dirty || $input.$touched || $input.$submitted) && $input.$invalid`
+ )
if block
block
- .input-tip
- +form-field-input(attributes=attributes)
+ if required
+ +form-field-feedback(name, 'required', multiple ? 'At least one option should be selected' : 'An option should be selected')
+
+mixin sane-ignite-form-field-dropdown({label, model, name, disabled = false, required = false, multiple = false, placeholder, placeholderEmpty, options, tip})
+ +ignite-form-field-dropdown(label, model, name, disabled, required, multiple, placeholder, placeholderEmpty, options, tip)&attributes(attributes)
+ if block
+ block
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/helpers/jade/form/form-field-feedback.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/helpers/jade/form/form-field-feedback.pug b/modules/web-console/frontend/app/helpers/jade/form/form-field-feedback.pug
index c70e7a3..2fa0a3c 100644
--- a/modules/web-console/frontend/app/helpers/jade/form/form-field-feedback.pug
+++ b/modules/web-console/frontend/app/helpers/jade/form/form-field-feedback.pug
@@ -15,18 +15,4 @@
limitations under the License.
mixin form-field-feedback(name, error, message)
- -var __field = `${form}[${name}]`
- -var __error = `${__field}.$error.${error}`
- -var __pristine = `${__field}.$pristine`
-
- i.fa.fa-exclamation-triangle.form-field-feedback(
- ng-if=`!${__pristine} && ${__error}`
- name=`{{ ${name} }}`
-
- bs-tooltip
- data-title=message
-
- ignite-error=error
- ignite-error-message=message
- ignite-restore-input-focus
- )
+ div(ng-message=error) #{message}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/helpers/jade/form/form-field-label.pug
----------------------------------------------------------------------
diff --git a/modules/web-console/frontend/app/helpers/jade/form/form-field-label.pug b/modules/web-console/frontend/app/helpers/jade/form/form-field-label.pug
index d0275c9..2edd115 100644
--- a/modules/web-console/frontend/app/helpers/jade/form/form-field-label.pug
+++ b/modules/web-console/frontend/app/helpers/jade/form/form-field-label.pug
@@ -14,10 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-mixin ignite-form-field__label(label, name, required)
+mixin ignite-form-field__label(label, name, required, disabled)
label.ignite-form-field__label(
id=`{{ ${name} }}Label`
for=`{{ ${name} }}Input`
- class=`{{ ${required} ? 'required' : '' }}`
+ ng-class=disabled && `{'ignite-form-field__label-disabled': ${disabled}}`
)
- span !{label}
+ span(class=`{{ ${required} ? 'required' : '' }}`) !{label}
+ if block
+ block
\ No newline at end of file