http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js b/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js index 4aad7e2..4bfbf48 100644 --- a/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js +++ b/modules/web-console/frontend/app/modules/states/configuration/summary/summary.controller.js @@ -20,8 +20,8 @@ import JSZip from 'jszip'; import saver from 'file-saver'; export default [ - '$rootScope', '$scope', '$http', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteLoading', '$filter', 'igniteConfigurationResource', 'JavaTypes', 'IgniteVersion', 'GeneratorDocker', 'GeneratorPom', 'IgniteFormUtils', - function($root, $scope, $http, LegacyUtils, Messages, Loading, $filter, Resource, JavaTypes, Version, docker, pom, FormUtils) { + '$rootScope', '$scope', '$http', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteLoading', '$filter', 'IgniteConfigurationResource', 'JavaTypes', 'IgniteVersion', 'IgniteConfigurationGenerator', 'SpringTransformer', 'JavaTransformer', 'GeneratorDocker', 'GeneratorPom', 'IgnitePropertiesGenerator', 'IgniteReadmeGenerator', 'IgniteFormUtils', + function($root, $scope, $http, LegacyUtils, Messages, Loading, $filter, Resource, JavaTypes, Version, generator, spring, java, docker, pom, propsGenerator, readme, FormUtils) { const ctrl = this; $scope.ui = { ready: false }; @@ -108,11 +108,18 @@ export default [ ] }; + const clnCfg = { type: 'file', name: 'client.xml' }; + const srvCfg = { type: 'file', name: 'server.xml' }; + const resourcesFolder = { type: 'folder', name: 'resources', children: [ - { type: 'file', name: 'secret.properties' } + { + type: 'folder', + name: 'META-INF', + children: [clnCfg, srvCfg] + } ] }; @@ -131,10 +138,6 @@ export default [ ] }; - const clnCfg = { type: 'file', name: 'client.xml' }; - - const srvCfg = { type: 'file', name: 'server.xml' }; - const mainFolder = { type: 'folder', name: 'main', @@ -147,11 +150,6 @@ export default [ children: [ { type: 'folder', - name: 'config', - children: [clnCfg, srvCfg] - }, - { - type: 'folder', name: 'jdbc-drivers', children: [ { type: 'file', name: 'README.txt' } @@ -215,6 +213,16 @@ export default [ folder.children.push(leaf); } + function cacheHasDatasource(cache) { + if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) { + const storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind]; + + return !!(storeFactory && (storeFactory.connectVia ? (storeFactory.connectVia === 'DataSource' ? storeFactory.dialect : false) : storeFactory.dialect)); // eslint-disable-line no-nested-ternary + } + + return false; + } + $scope.selectItem = (cluster) => { delete ctrl.cluster; @@ -231,17 +239,17 @@ export default [ sessionStorage.summarySelectedId = $scope.clusters.indexOf(cluster); - mainFolder.children = [javaFolder]; + mainFolder.children = [javaFolder, resourcesFolder]; if (_.find(cluster.caches, (cache) => !_.isNil(cache.cacheStoreFactory))) javaFolder.children = [javaConfigFolder, loadFolder, javaStartupFolder]; else javaFolder.children = [javaConfigFolder, javaStartupFolder]; - if ($generatorCommon.secretPropertiesNeeded(cluster)) - mainFolder.children.push(resourcesFolder); + if (_.nonNil(_.find(cluster.caches, cacheHasDatasource)) || cluster.sslEnabled) + resourcesFolder.children.push({ type: 'file', name: 'secret.properties' }); - if ($generatorJava.isDemoConfigured(cluster, $root.IgniteDemoMode)) + if (java.isDemoConfigured(cluster, $root.IgniteDemoMode)) javaFolder.children.push(demoFolder); if (cluster.discovery.kind === 'Jdbc' && cluster.discovery.Jdbc.dialect) @@ -286,7 +294,6 @@ export default [ // TODO IGNITE-2114: implemented as independent logic for download. $scope.downloadConfiguration = function() { const cluster = $scope.cluster; - const clientNearCfg = cluster.clientNearCfg; const zip = new JSZip(); @@ -299,54 +306,65 @@ export default [ zip.file('Dockerfile', ctrl.data.docker); zip.file('.dockerignore', docker.ignoreFile()); - const builder = $generatorProperties.generateProperties(cluster); + const cfg = generator.igniteConfiguration(cluster, false); + const clientCfg = generator.igniteConfiguration(cluster, true); + const clientNearCaches = _.filter(cluster.caches, (cache) => _.get(cache, 'clientNearConfiguration.enabled')); + + const secProps = propsGenerator.generate(cfg); - if (builder) - zip.file('src/main/resources/secret.properties', builder.asString()); + if (secProps) + zip.file('src/main/resources/secret.properties', secProps); - const srcPath = 'src/main/java/'; + const srcPath = 'src/main/java'; + const resourcesPath = 'src/main/resources'; - const serverXml = 'config/' + cluster.name + '-server.xml'; - const clientXml = 'config/' + cluster.name + '-client.xml'; + const serverXml = `${cluster.name}-server.xml`; + const clientXml = `${cluster.name}-client.xml`; - zip.file(serverXml, $generatorXml.cluster(cluster)); - zip.file(clientXml, $generatorXml.cluster(cluster, clientNearCfg)); + const metaPath = `${resourcesPath}/META-INF`; - zip.file(srcPath + 'config/ServerConfigurationFactory.java', $generatorJava.cluster(cluster, 'config', 'ServerConfigurationFactory', null)); - zip.file(srcPath + 'config/ClientConfigurationFactory.java', $generatorJava.cluster(cluster, 'config', 'ClientConfigurationFactory', clientNearCfg)); + zip.file(`${metaPath}/${serverXml}`, spring.igniteConfiguration(cfg).asString()); + zip.file(`${metaPath}/${clientXml}`, spring.igniteConfiguration(clientCfg, clientNearCaches).asString()); - if ($generatorJava.isDemoConfigured(cluster, $root.IgniteDemoMode)) { - zip.file(srcPath + 'demo/DemoStartup.java', $generatorJava.nodeStartup(cluster, 'demo', 'DemoStartup', + const cfgPath = `${srcPath}/config`; + + zip.file(`${cfgPath}/ServerConfigurationFactory.java`, java.igniteConfiguration(cfg, 'config', 'ServerConfigurationFactory').asString()); + zip.file(`${cfgPath}/ClientConfigurationFactory.java`, java.igniteConfiguration(cfg, 'config', 'ClientConfigurationFactory', clientNearCaches).asString()); + + if (java.isDemoConfigured(cluster, $root.IgniteDemoMode)) { + zip.file(`${srcPath}/demo/DemoStartup.java`, java.nodeStartup(cluster, 'demo.DemoStartup', 'ServerConfigurationFactory.createConfiguration()', 'config.ServerConfigurationFactory')); } // Generate loader for caches with configured store. - const cachesToLoad = _.filter(cluster.caches, (cache) => !_.isNil(cache.cacheStoreFactory)); + const cachesToLoad = _.filter(cluster.caches, (cache) => _.nonNil(cache.cacheStoreFactory)); + + if (_.nonEmpty(cachesToLoad)) + zip.file(`${srcPath}/load/LoadCaches.java`, java.loadCaches(cachesToLoad, 'load', 'LoadCaches', `"${clientXml}"`)); - if (!_.isEmpty(cachesToLoad)) - zip.file(srcPath + 'load/LoadCaches.java', $generatorJava.loadCaches(cachesToLoad, 'load', 'LoadCaches', '"' + clientXml + '"')); + const startupPath = `${srcPath}/startup`; - zip.file(srcPath + 'startup/ServerNodeSpringStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ServerNodeSpringStartup', '"' + serverXml + '"')); - zip.file(srcPath + 'startup/ClientNodeSpringStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ClientNodeSpringStartup', '"' + clientXml + '"')); + zip.file(`${startupPath}/ServerNodeSpringStartup.java`, java.nodeStartup(cluster, 'startup.ServerNodeSpringStartup', `"${serverXml}"`)); + zip.file(`${startupPath}/ClientNodeSpringStartup.java`, java.nodeStartup(cluster, 'startup.ClientNodeSpringStartup', `"${clientXml}"`)); - zip.file(srcPath + 'startup/ServerNodeCodeStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ServerNodeCodeStartup', + zip.file(`${startupPath}/ServerNodeCodeStartup.java`, java.nodeStartup(cluster, 'startup.ServerNodeCodeStartup', 'ServerConfigurationFactory.createConfiguration()', 'config.ServerConfigurationFactory')); - zip.file(srcPath + 'startup/ClientNodeCodeStartup.java', $generatorJava.nodeStartup(cluster, 'startup', 'ClientNodeCodeStartup', - 'ClientConfigurationFactory.createConfiguration()', 'config.ClientConfigurationFactory', clientNearCfg)); + zip.file(`${startupPath}/ClientNodeCodeStartup.java`, java.nodeStartup(cluster, 'startup.ClientNodeCodeStartup', + 'ClientConfigurationFactory.createConfiguration()', 'config.ClientConfigurationFactory', clientNearCaches)); - zip.file('pom.xml', pom.generate(cluster, Version.ignite).asString()); + zip.file('pom.xml', pom.generate(cluster, Version.productVersion().ignite).asString()); - zip.file('README.txt', $generatorReadme.readme().asString()); - zip.file('jdbc-drivers/README.txt', $generatorReadme.readmeJdbc().asString()); + zip.file('README.txt', readme.generate()); + zip.file('jdbc-drivers/README.txt', readme.generateJDBC()); - if (!ctrl.data.pojos) - ctrl.data.pojos = $generatorJava.pojos(cluster.caches); + if (_.isEmpty(ctrl.data.pojos)) + ctrl.data.pojos = java.pojos(cluster.caches); for (const pojo of ctrl.data.pojos) { if (pojo.keyClass && JavaTypes.nonBuiltInClass(pojo.keyType)) - zip.file(srcPath + pojo.keyType.replace(/\./g, '/') + '.java', pojo.keyClass); + zip.file(`${srcPath}/${pojo.keyType.replace(/\./g, '/')}.java`, pojo.keyClass); - zip.file(srcPath + pojo.valueType.replace(/\./g, '/') + '.java', pojo.valueClass); + zip.file(`${srcPath}/${pojo.valueType.replace(/\./g, '/')}.java`, pojo.valueClass); } $generatorOptional.optionalContent(zip, cluster);
http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/ErrorPopover.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/services/ErrorPopover.service.js b/modules/web-console/frontend/app/services/ErrorPopover.service.js index 3130431..5132d50 100644 --- a/modules/web-console/frontend/app/services/ErrorPopover.service.js +++ b/modules/web-console/frontend/app/services/ErrorPopover.service.js @@ -108,7 +108,7 @@ export default class ErrorPopover { if (this._popover) this._popover.hide(); - if (ui) { + if (ui && ui.isPanelLoaded) { this.FormUtils.ensureActivePanel(ui, panelId, id); this.$timeout(() => this._show(id, message, showTime), ui.isPanelLoaded(panelId) ? 200 : 500); http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/FormUtils.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/services/FormUtils.service.js b/modules/web-console/frontend/app/services/FormUtils.service.js index 5e7943a..6ccc3c6 100644 --- a/modules/web-console/frontend/app/services/FormUtils.service.js +++ b/modules/web-console/frontend/app/services/FormUtils.service.js @@ -17,7 +17,7 @@ export default ['IgniteFormUtils', ['$window', 'IgniteFocus', ($window, Focus) => { function ensureActivePanel(ui, pnl, focusId) { - if (ui) { + if (ui && ui.loadPanel) { const collapses = $('div.panel-collapse'); ui.loadPanel(pnl); @@ -430,6 +430,10 @@ export default ['IgniteFormUtils', ['$window', 'IgniteFocus', ($window, Focus) = return _.includes(this.loadedPanels, pnl); } }; + }, + markPristineInvalidAsDirty(ngModelCtrl) { + if (ngModelCtrl && ngModelCtrl.$invalid && ngModelCtrl.$pristine) + ngModelCtrl.$setDirty(); } }; }]]; http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/JavaTypes.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/services/JavaTypes.service.js b/modules/web-console/frontend/app/services/JavaTypes.service.js index 8cb87be..679914f 100644 --- a/modules/web-console/frontend/app/services/JavaTypes.service.js +++ b/modules/web-console/frontend/app/services/JavaTypes.service.js @@ -15,8 +15,6 @@ * limitations under the License. */ -import _ from 'lodash'; - // Java built-in class names. import JAVA_CLASSES from '../data/java-classes.json'; @@ -42,6 +40,42 @@ const VALID_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}- * Utility service for various check on java types. */ export default class JavaTypes { + static $inject = ['igniteClusterDefaults', 'igniteCacheDefaults', 'igniteIgfsDefaults']; + + constructor(clusterDflts, cacheDflts, igfsDflts) { + this.enumClasses = _.uniq(this._enumClassesAcc(_.merge(clusterDflts, cacheDflts, igfsDflts), [])); + this.shortEnumClasses = _.map(this.enumClasses, (cls) => this.shortClassName(cls)); + } + + /** + * Collects recursive enum classes. + * + * @param root Root object. + * @param classes Collected classes. + * @return {Array.<String>} + * @private + */ + _enumClassesAcc(root, classes) { + return _.reduce(root, (acc, val, key) => { + if (key === 'clsName') + acc.push(val); + else if (_.isObject(val)) + this._enumClassesAcc(val, acc); + + return acc; + }, classes); + } + + /** + * Check if class name is non enum class in Ignite configuration. + * + * @param clsName + * @return {boolean} + */ + nonEnum(clsName) { + return !_.includes(this.shortEnumClasses, clsName) && !_.includes(this.enumClasses, clsName); + } + /** * @param clsName {String} Class name to check. * @returns {boolean} 'true' if provided class name is a not Java built in class. @@ -52,7 +86,7 @@ export default class JavaTypes { /** * @param clsName Class name to check. - * @returns Full class name for java build-in types or source class otherwise. + * @returns {String} Full class name for java build-in types or source class otherwise. */ fullClassName(clsName) { const type = _.find(JAVA_CLASSES, (clazz) => clsName === clazz.short); @@ -61,6 +95,23 @@ export default class JavaTypes { } /** + * Extract class name from full class name. + * + * @param clsName full class name. + * @return {String} Class name. + */ + shortClassName(clsName) { + if (this.isJavaPrimitive(clsName)) + return clsName; + + const fullClsName = this.fullClassName(clsName); + + const dotIdx = fullClsName.lastIndexOf('.'); + + return dotIdx > 0 ? fullClsName.substr(dotIdx + 1) : fullClsName; + } + + /** * @param value {String} Value text to check. * @returns {boolean} 'true' if given text is valid Java class name. */ @@ -115,4 +166,17 @@ export default class JavaTypes { isJavaPrimitive(clsName) { return _.includes(JAVA_PRIMITIVES, clsName); } + + /** + * Convert some name to valid java name. + * + * @param prefix To append to java name. + * @param name to convert. + * @returns {string} Valid java name. + */ + toJavaName(prefix, name) { + const javaName = name ? this.shortClassName(name).replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt'; + + return prefix + javaName.charAt(0).toLocaleUpperCase() + javaName.slice(1); + } } http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/LegacyTable.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/services/LegacyTable.service.js b/modules/web-console/frontend/app/services/LegacyTable.service.js index 5d9ec9d..a024a3b 100644 --- a/modules/web-console/frontend/app/services/LegacyTable.service.js +++ b/modules/web-console/frontend/app/services/LegacyTable.service.js @@ -19,7 +19,27 @@ export default ['IgniteLegacyTable', ['IgniteLegacyUtils', 'IgniteFocus', 'IgniteErrorPopover', (LegacyUtils, Focus, ErrorPopover) => { function _model(item, field) { - return LegacyUtils.getModel(item, field); + let path = field.path; + + if (_.isNil(path) || _.isNil(item)) + return item; + + path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties + path = path.replace(/^\./, ''); // strip a leading dot + + const segs = path.split('.'); + let root = item; + + while (segs.length > 0) { + const pathStep = segs.shift(); + + if (typeof root[pathStep] === 'undefined') + root[pathStep] = {}; + + root = root[pathStep]; + } + + return root; } const table = {name: 'none', editIndex: -1}; @@ -190,7 +210,7 @@ export default ['IgniteLegacyTable', } } - return valid; + return valid || stopEdit; }, tablePairSaveVisible(field, index) { const pairValue = _tablePairValue(field, index); http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/LegacyUtils.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/services/LegacyUtils.service.js b/modules/web-console/frontend/app/services/LegacyUtils.service.js index dcf0bc8..e7c064b 100644 --- a/modules/web-console/frontend/app/services/LegacyUtils.service.js +++ b/modules/web-console/frontend/app/services/LegacyUtils.service.js @@ -182,43 +182,19 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { const VALID_JAVA_IDENTIFIER = new RegExp('^[a-zA-Z_$][a-zA-Z\\d_$]*$'); - function isValidJavaIdentifier(msg, ident, elemId, panels, panelId) { + function isValidJavaIdentifier(msg, ident, elemId, panels, panelId, stopEdit) { if (isEmptyString(ident)) - return ErrorPopover.show(elemId, msg + ' is invalid!', panels, panelId); + return !stopEdit && ErrorPopover.show(elemId, msg + ' is invalid!', panels, panelId); if (_.includes(JAVA_KEYWORDS, ident)) - return ErrorPopover.show(elemId, msg + ' could not contains reserved java keyword: "' + ident + '"!', panels, panelId); + return !stopEdit && ErrorPopover.show(elemId, msg + ' could not contains reserved java keyword: "' + ident + '"!', panels, panelId); if (!VALID_JAVA_IDENTIFIER.test(ident)) - return ErrorPopover.show(elemId, msg + ' contains invalid identifier: "' + ident + '"!', panels, panelId); + return !stopEdit && ErrorPopover.show(elemId, msg + ' contains invalid identifier: "' + ident + '"!', panels, panelId); return true; } - function getModel(obj, field) { - let path = field.path; - - if (!isDefined(path) || !isDefined(obj)) - return obj; - - path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties - path = path.replace(/^\./, ''); // strip a leading dot - - const segs = path.split('.'); - let root = obj; - - while (segs.length > 0) { - const pathStep = segs.shift(); - - if (typeof root[pathStep] === 'undefined') - root[pathStep] = {}; - - root = root[pathStep]; - } - - return root; - } - /** * Extract datasource from cache or cluster. * @@ -226,18 +202,26 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { * @returns {*} Datasource object or null if not set. */ function extractDataSource(object) { + let datasource = null; + // Extract from cluster object if (_.get(object, 'discovery.kind') === 'Jdbc') { - const datasource = object.discovery.Jdbc; + datasource = object.discovery.Jdbc; + + if (datasource.dataSourceBean && datasource.dialect) + return datasource; + } // Extract from JDBC checkpoint configuration. + else if (_.get(object, 'kind') === 'JDBC') { + datasource = object.JDBC; if (datasource.dataSourceBean && datasource.dialect) return datasource; } // Extract from cache object else if (_.get(object, 'cacheStoreFactory.kind')) { - const storeFactory = object.cacheStoreFactory[object.cacheStoreFactory.kind]; + datasource = object.cacheStoreFactory[object.cacheStoreFactory.kind]; - if (storeFactory.dialect || (storeFactory.connectVia === 'DataSource')) - return storeFactory; + if (datasource.dialect || (datasource.connectVia === 'DataSource')) + return datasource; } return null; @@ -268,10 +252,13 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { * Compare datasources of caches or clusters. * * @param firstObj First cache or cluster. + * @param firstType Type of first object to compare. * @param secondObj Second cache or cluster. + * @param secondType Type of first object to compare. + * @param index Index of invalid object when check is failed. * @returns {*} Check result object. */ - function compareDataSources(firstObj, secondObj) { + function compareDataSources(firstObj, firstType, secondObj, secondType, index) { const firstDs = extractDataSource(firstObj); const secondDs = extractDataSource(secondObj); @@ -280,7 +267,7 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { const secondDB = secondDs.dialect; if (firstDs.dataSourceBean === secondDs.dataSourceBean && firstDB !== secondDB) - return {checked: false, firstObj, firstDB, secondObj, secondDB}; + return {checked: false, firstObj, firstDs, firstType, secondObj, secondDs, secondType, index}; } return DS_CHECK_SUCCESS; @@ -303,7 +290,6 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { } return { - getModel, mkOptions(options) { return _.map(options, (option) => { return {value: option, label: isDefined(option) ? option : 'Not set'}; @@ -326,24 +312,24 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { javaBuiltInTypes, isJavaBuiltInClass, isValidJavaIdentifier, - isValidJavaClass(msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId) { + isValidJavaClass(msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId, stopEdit = false) { if (isEmptyString(ident)) - return ErrorPopover.show(elemId, msg + ' could not be empty!', panels, panelId); + return !stopEdit && ErrorPopover.show(elemId, msg + ' could not be empty!', panels, panelId); const parts = ident.split('.'); const len = parts.length; if (!allowBuiltInClass && isJavaBuiltInClass(ident)) - return ErrorPopover.show(elemId, msg + ' should not be the Java build-in class!', panels, panelId); + return !stopEdit && ErrorPopover.show(elemId, msg + ' should not be the Java build-in class!', panels, panelId); if (len < 2 && !isJavaBuiltInClass(ident) && !packageOnly) - return ErrorPopover.show(elemId, msg + ' does not have package specified!', panels, panelId); + return !stopEdit && ErrorPopover.show(elemId, msg + ' does not have package specified!', panels, panelId); for (let i = 0; i < parts.length; i++) { const part = parts[i]; - if (!isValidJavaIdentifier(msg, part, elemId, panels, panelId)) + if (!isValidJavaIdentifier(msg, part, elemId, panels, panelId, stopEdit)) return false; } @@ -394,14 +380,25 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { let res = DS_CHECK_SUCCESS; _.find(caches, (curCache, curIx) => { - res = compareDataSources(curCache, cluster); + // Check datasources of cluster JDBC ip finder and cache store factory datasource. + res = compareDataSources(curCache, 'cache', cluster, 'cluster'); + + if (!res.checked) + return true; + + _.find(cluster.checkpointSpi, (spi, spiIx) => { + res = compareDataSources(curCache, 'cache', spi, 'checkpoint', spiIx); + + return !res.checked; + }); if (!res.checked) return true; + // Check datasource of current saved cache and datasource of other cache in cluster. if (isDefined(checkCacheExt)) { if (checkCacheExt._id !== curCache._id) { - res = compareDataSources(checkCacheExt, curCache); + res = compareDataSources(checkCacheExt, 'cache', curCache, 'cache'); return !res.checked; } @@ -409,9 +406,10 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { return false; } + // Check datasources of specified list of caches. return _.find(caches, (checkCache, checkIx) => { if (checkIx < curIx) { - res = compareDataSources(checkCache, curCache); + res = compareDataSources(checkCache, 'cache', curCache, 'cache'); return !res.checked; } @@ -420,6 +418,26 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { }); }); + if (res.checked) { + _.find(cluster.checkpointSpi, (curSpi, curIx) => { + // Check datasources of cluster JDBC ip finder and cache store factory datasource. + res = compareDataSources(cluster, 'cluster', curSpi, 'checkpoint', curIx); + + if (!res.checked) + return true; + + _.find(cluster.checkpointSpi, (spi, spiIx) => { + if (spiIx < curIx) { + res = compareDataSources(curSpi, 'checkpoint', spi, 'checkpoint', curIx); + + return !res.checked; + } + + return false; + }); + }); + } + return res; }, checkCacheSQLSchemas(caches, checkCacheExt) { @@ -469,14 +487,8 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { writeThrough: dflt || cache.writeThrough }; } - }, - autoClusterSwapSpiConfiguration(cluster, caches) { - const swapConfigured = cluster.swapSpaceSpi && cluster.swapSpaceSpi.kind; - - if (!swapConfigured && _.find(caches, (cache) => cache.swapEnabled)) - return {swapSpaceSpi: {kind: 'FileSwapSpaceSpi'}}; - return null; + return {}; }, randomString(len) { const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; @@ -498,7 +510,10 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { const firstErrorKey = errKeys[0]; const firstError = errors[firstErrorKey][0]; - const actualError = firstError.$error[firstErrorKey][0]; + + const err = firstError.$error[firstErrorKey]; + + const actualError = _.isArray(err) ? err[0] : firstError; const errNameFull = actualError.$name; const errNameShort = errNameFull.endsWith('TextInput') ? errNameFull.substring(0, errNameFull.length - 9) : errNameFull; @@ -507,12 +522,17 @@ export default ['IgniteLegacyUtils', ['IgniteErrorPopover', (ErrorPopover) => { try { return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey]; } - catch (ignored) { + catch (ignored1) { try { return form[firstError.$name].$errorMessages[errName][firstErrorKey]; } - catch (ignited) { - return false; + catch (ignored2) { + try { + return form.$errorMessages[errName][firstErrorKey]; + } + catch (ignored3) { + return false; + } } } }; http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/services/SqlTypes.service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/services/SqlTypes.service.js b/modules/web-console/frontend/app/services/SqlTypes.service.js index 2a16e9d..e42d903 100644 --- a/modules/web-console/frontend/app/services/SqlTypes.service.js +++ b/modules/web-console/frontend/app/services/SqlTypes.service.js @@ -15,13 +15,11 @@ * limitations under the License. */ -import _ from 'lodash'; - // List of H2 reserved SQL keywords. -import H2_SQL_KEYWORDS from '../data/sql-keywords.json'; +import H2_SQL_KEYWORDS from 'app/data/sql-keywords.json'; // List of JDBC type descriptors. -import JDBC_TYPES from '../data/jdbc-types.json'; +import JDBC_TYPES from 'app/data/jdbc-types.json'; // Regular expression to check H2 SQL identifier. const VALID_IDENTIFIER = /^[a-zA-Z_][a-zA-Z0-9_$]*$/im; http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/app/vendor.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/vendor.js b/modules/web-console/frontend/app/vendor.js index 0322887..a9e8844 100644 --- a/modules/web-console/frontend/app/vendor.js +++ b/modules/web-console/frontend/app/vendor.js @@ -38,6 +38,7 @@ import 'brace'; import 'brace/mode/xml'; import 'brace/mode/sql'; import 'brace/mode/java'; +import 'brace/mode/csharp'; import 'brace/mode/dockerfile'; import 'brace/mode/snippets'; import 'brace/theme/chrome'; @@ -46,7 +47,7 @@ import 'brace/ext/searchbox'; import 'file-saver'; import 'jszip'; import 'nvd3'; -import 'query-command-supported'; +import 'lodash'; import 'angular-gridster/dist/angular-gridster.min.css'; import 'angular-tree-control/css/tree-control-attribute.css'; import 'angular-tree-control/css/tree-control.css'; http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/caches-controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/controllers/caches-controller.js b/modules/web-console/frontend/controllers/caches-controller.js index 8c32906..8c01173 100644 --- a/modules/web-console/frontend/controllers/caches-controller.js +++ b/modules/web-console/frontend/controllers/caches-controller.js @@ -17,8 +17,8 @@ // Controller for Caches screen. export default ['cachesController', [ - '$scope', '$http', '$state', '$filter', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', - function($scope, $http, $state, $filter, $timeout, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, Resource, ErrorPopover, FormUtils) { + '$scope', '$http', '$state', '$filter', '$timeout', '$modal', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'IgniteLegacyTable', + function($scope, $http, $state, $filter, $timeout, $modal, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, Resource, ErrorPopover, FormUtils, LegacyTable) { UnsavedChangesGuard.install($scope); const emptyCache = {empty: true}; @@ -96,6 +96,73 @@ export default ['cachesController', [ item.offHeapMaxMemory = item.offHeapMaxMemory > 0 ? item.offHeapMaxMemory : null; }; + $scope.tablePairSave = LegacyTable.tablePairSave; + $scope.tablePairSaveVisible = LegacyTable.tablePairSaveVisible; + $scope.tableNewItem = LegacyTable.tableNewItem; + $scope.tableNewItemActive = LegacyTable.tableNewItemActive; + + $scope.tableStartEdit = function(item, field, index) { + if ($scope.tableReset(true)) + LegacyTable.tableStartEdit(item, field, index, $scope.tableSave); + }; + + $scope.tableEditing = LegacyTable.tableEditing; + + $scope.tableSave = function(field, index, stopEdit) { + if (LegacyTable.tablePairSaveVisible(field, index)) + return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit); + + return true; + }; + + $scope.tableRemove = function(item, field, index) { + if ($scope.tableReset(true)) + LegacyTable.tableRemove(item, field, index); + }; + + $scope.tableReset = (trySave) => { + const field = LegacyTable.tableField(); + + if (trySave && LegacyUtils.isDefined(field) && !$scope.tableSave(field, LegacyTable.tableEditedRowIndex(), true)) + return false; + + LegacyTable.tableReset(); + + return true; + }; + + $scope.hibernatePropsTbl = { + type: 'hibernate', + model: 'cacheStoreFactory.CacheHibernateBlobStoreFactory.hibernateProperties', + focusId: 'Property', + ui: 'table-pair', + keyName: 'name', + valueName: 'value', + save: $scope.tableSave + }; + + $scope.tablePairValid = function(item, field, index, stopEdit) { + const pairValue = LegacyTable.tablePairValue(field, index); + + const model = _.get(item, field.model); + + if (!_.isNil(model)) { + const idx = _.findIndex(model, (pair) => { + return pair.name === pairValue.key; + }); + + // Found duplicate by key. + if (idx >= 0 && idx !== index) { + if (stopEdit) + return false; + + return ErrorPopover.show(LegacyTable.tableFieldId(index, 'KeyProperty'), 'Property with such name already exists!', $scope.ui, 'query'); + } + } + + return true; + }; + Loading.start('loadingCachesScreen'); // When landing on the page, get caches and show them. @@ -117,6 +184,7 @@ export default ['cachesController', [ value: cluster._id, label: cluster.name, discovery: cluster.discovery, + checkpointSpi: cluster.checkpointSpi, caches: cluster.caches })); @@ -204,7 +272,7 @@ export default ['cachesController', [ else $scope.backupItem = emptyCache; - $scope.backupItem = angular.merge({}, blank, $scope.backupItem); + $scope.backupItem = _.merge({}, blank, $scope.backupItem); if ($scope.ui.inputForm) { $scope.ui.inputForm.$error = {}; @@ -258,6 +326,15 @@ export default ['cachesController', [ return caches; } + const _objToString = (type, name, prefix = '') => { + if (type === 'checkpoint') + return `${prefix} checkpoint configuration in cluster "${name}"`; + if (type === 'cluster') + return `${prefix} discovery IP finder in cluster "${name}"`; + + return `${prefix} ${type} "${name}"`; + }; + function checkDataSources() { const clusters = cacheClusters(); @@ -272,20 +349,11 @@ export default ['cachesController', [ }); if (!checkRes.checked) { - if (_.get(checkRes.secondObj, 'discovery.kind') === 'Jdbc') { - return ErrorPopover.show(checkRes.firstObj.cacheStoreFactory.kind === 'CacheJdbcPojoStoreFactory' ? 'pojoDialectInput' : 'blobDialectInput', - 'Found cluster "' + failCluster.label + '" with the same data source bean name "' + - checkRes.secondObj.discovery.Jdbc.dataSourceBean + '" and different database: "' + - LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in current cache and "' + - LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in"' + checkRes.secondObj.label + '" cluster', - $scope.ui, 'store', 10000); - } - return ErrorPopover.show(checkRes.firstObj.cacheStoreFactory.kind === 'CacheJdbcPojoStoreFactory' ? 'pojoDialectInput' : 'blobDialectInput', - 'Found cache "' + checkRes.secondObj.name + '" in cluster "' + failCluster.label + '" ' + - 'with the same data source bean name "' + checkRes.firstObj.cacheStoreFactory[checkRes.firstObj.cacheStoreFactory.kind].dataSourceBean + - '" and different database: "' + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in current cache and "' + - LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in "' + checkRes.secondObj.name + '" cache', + 'Found ' + _objToString(checkRes.secondType, checkRes.secondObj.name || failCluster.label) + ' with the same data source bean name "' + + checkRes.firstDs.dataSourceBean + '" and different database: "' + + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name, 'current') + ' and "' + + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name || failCluster.label), $scope.ui, 'store', 10000); } @@ -296,7 +364,7 @@ export default ['cachesController', [ if (evictionPlc && evictionPlc.kind) { const plc = evictionPlc[evictionPlc.kind]; - if (plc.maxMemorySize === 0 && plc.maxSize === 0) + if (plc && !plc.maxMemorySize && !plc.maxSize) return ErrorPopover.show('evictionPolicymaxMemorySizeInput', 'Either maximum memory size or maximum size should be great than 0!', $scope.ui, 'memory'); } @@ -409,7 +477,7 @@ export default ['cachesController', [ }); if (idx >= 0) - angular.merge($scope.caches[idx], item); + _.assign($scope.caches[idx], item); else { item._id = _id; $scope.caches.push(item); @@ -440,7 +508,7 @@ export default ['cachesController', [ $scope.saveItem = function() { const item = $scope.backupItem; - angular.extend(item, LegacyUtils.autoCacheStoreConfiguration(item, cacheDomains(item))); + _.merge(item, LegacyUtils.autoCacheStoreConfiguration(item, cacheDomains(item))); if (validate(item)) save(item); @@ -462,7 +530,20 @@ export default ['cachesController', [ item.name = newName; - delete item.sqlSchema; + if (!_.isEmpty(item.clusters) && !_.isNil(item.sqlSchema)) { + delete item.sqlSchema; + + const scope = $scope.$new(); + + scope.title = 'Info'; + scope.content = [ + 'Use the same SQL schema name in one cluster in not allowed', + 'SQL schema name will be reset' + ]; + + // Show a basic modal from a controller + $modal({scope, template: '/templates/message.html', placement: 'center', show: true}); + } save(item); }); http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/clusters-controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/controllers/clusters-controller.js b/modules/web-console/frontend/controllers/clusters-controller.js index 5a3c7e2..f9096f7 100644 --- a/modules/web-console/frontend/controllers/clusters-controller.js +++ b/modules/web-console/frontend/controllers/clusters-controller.js @@ -17,7 +17,7 @@ // Controller for Clusters screen. export default ['clustersController', [ - '$rootScope', '$scope', '$http', '$state', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'igniteEventGroups', 'DemoInfo', 'IgniteLegacyTable', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', + '$rootScope', '$scope', '$http', '$state', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'igniteEventGroups', 'DemoInfo', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', function($root, $scope, $http, $state, $timeout, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, igniteEventGroups, DemoInfo, LegacyTable, Resource, ErrorPopover, FormUtils) { UnsavedChangesGuard.install($scope); @@ -103,6 +103,46 @@ export default ['clustersController', [ else $scope.backupItem.failoverSpi = {}; } + else if (field.type === 'loadBalancingSpi') { + const newLoadBalancing = {Adaptive: { + loadProbe: { + Job: {useAverage: true}, + CPU: { + useAverage: true, + useProcessors: true + }, + ProcessingTime: {useAverage: true} + } + }}; + + if (LegacyUtils.isDefined($scope.backupItem.loadBalancingSpi)) + $scope.backupItem.loadBalancingSpi.push(newLoadBalancing); + else + $scope.backupItem.loadBalancingSpi = [newLoadBalancing]; + } + else if (field.type === 'checkpointSpi') { + const newCheckpointCfg = { + FS: { + directoryPaths: [] + }, + S3: { + awsCredentials: { + kind: 'Basic' + }, + clientConfiguration: { + retryPolicy: { + kind: 'Default' + }, + useReaper: true + } + } + }; + + if (LegacyUtils.isDefined($scope.backupItem.checkpointSpi)) + $scope.backupItem.checkpointSpi.push(newCheckpointCfg); + else + $scope.backupItem.checkpointSpi = [newCheckpointCfg]; + } else LegacyTable.tableNewItem(field); } @@ -149,6 +189,8 @@ export default ['clustersController', [ $scope.backupItem.failoverSpi.splice(idx, 1); }; + $scope.supportedJdbcTypes = LegacyUtils.mkOptions(LegacyUtils.SUPPORTED_JDBC_TYPES); + // We need to initialize backupItem with empty object in order to properly used from angular directives. $scope.backupItem = emptyCluster; @@ -204,11 +246,20 @@ export default ['clustersController', [ // When landing on the page, get clusters and show them. Resource.read() - .then(({spaces, clusters, caches, igfss}) => { + .then(({spaces, clusters, caches, domains, igfss}) => { $scope.spaces = spaces; + $scope.clusters = clusters; - $scope.caches = _.map(caches, (cache) => ({value: cache._id, label: cache.name, cache})); + $scope.caches = _.map(caches, (cache) => { + cache.domains = _.filter(domains, ({_id}) => _.includes(cache.domains, _id)); + + if (_.get(cache, 'nodeFilter.kind') === 'IGFS') + cache.nodeFilter.IGFS.instance = _.find(igfss, {_id: cache.nodeFilter.IGFS.igfs}); + + return {value: cache._id, label: cache.name, cache}; + }); + $scope.igfss = _.map(igfss, (igfs) => ({value: igfs._id, label: igfs.name, igfs})); _.forEach($scope.clusters, (cluster) => { @@ -222,6 +273,9 @@ export default ['clustersController', [ if (!cluster.logger) cluster.logger = {Log4j: { mode: 'Default'}}; + + if (!cluster.eventStorage) + cluster.eventStorage = { kind: 'Memory' }; }); if ($state.params.linkId) @@ -259,6 +313,12 @@ export default ['clustersController', [ form.$setPristine(); else form.$setDirty(); + + $scope.clusterCaches = _.filter($scope.caches, + (cache) => _.find($scope.backupItem.caches, + (selCache) => selCache === cache.value + ) + ); }, true); $scope.$watch('ui.activePanels.length', () => { @@ -279,6 +339,8 @@ export default ['clustersController', [ Loading.finish('loadingClustersScreen'); }); + $scope.clusterCaches = []; + $scope.selectItem = function(item, backup) { function selectItem() { $scope.selectedItem = item; @@ -300,7 +362,7 @@ export default ['clustersController', [ else $scope.backupItem = emptyCluster; - $scope.backupItem = angular.merge({}, blank, $scope.backupItem); + $scope.backupItem = _.merge({}, blank, $scope.backupItem); if ($scope.ui.inputForm) { $scope.ui.inputForm.$error = {}; @@ -319,7 +381,7 @@ export default ['clustersController', [ $scope.linkId = () => $scope.backupItem._id ? $scope.backupItem._id : 'create'; function prepareNewItem(linkId) { - return angular.merge({}, blank, { + return _.merge({}, blank, { space: $scope.spaces[0]._id, discovery: { kind: 'Multicast', @@ -331,6 +393,7 @@ export default ['clustersController', [ communication: {tcpNoDelay: true}, connector: {noDelay: true}, collision: {kind: 'Noop', JobStealing: {stealingEnabled: true}, PriorityQueue: {starvationPreventionEnabled: true}}, + eventStorage: {kind: 'Memory'}, failoverSpi: [], logger: {Log4j: { mode: 'Default'}}, caches: linkId && _.find($scope.caches, {value: linkId}) ? [linkId] : [], @@ -354,27 +417,45 @@ export default ['clustersController', [ (cache) => _.includes(item.caches, cache._id)); } + const _objToString = (type, name, prefix = '') => { + if (type === 'checkpoint') + return prefix + ' checkpoint configuration'; + if (type === 'cluster') + return prefix + ' discovery IP finder'; + + return `${prefix} ${type} "${name}"`; + }; + function checkCacheDatasources(item) { const caches = clusterCaches(item); const checkRes = LegacyUtils.checkDataSources(item, caches); if (!checkRes.checked) { - if (_.get(checkRes.secondObj, 'discovery.kind') === 'Jdbc') { - return ErrorPopover.show('dialectInput', - 'Found cache "' + checkRes.firstObj.name + '" with the same data source bean name "' + - item.discovery.Jdbc.dataSourceBean + '" and different database: "' + - LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in current cluster and "' + - LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in "' + checkRes.firstObj.name + '" cache', - $scope.ui, 'general', 10000); + let ids; + + if (checkRes.secondType === 'cluster') + ids = { section: 'general', fieldId: 'dialectInput' }; + else if (checkRes.secondType === 'cache') + ids = { section: 'general', fieldId: 'cachesInput' }; + else if (checkRes.secondType === 'checkpoint') + ids = { section: 'checkpoint', fieldId: `checkpointJdbcDialect${checkRes.index}Input` }; + else + return true; + + if (checkRes.firstType === checkRes.secondType && checkRes.firstType === 'cache') { + return ErrorPopover.show(ids.fieldId, 'Found caches "' + checkRes.firstObj.name + '" and "' + checkRes.secondObj.name + '" with the same data source bean name "' + + checkRes.firstDs.dataSourceBean + '" and different database: "' + + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name) + ' and "' + + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name), + $scope.ui, ids.section, 10000); } - return ErrorPopover.show('cachesInput', - 'Found caches "' + checkRes.firstObj.name + '" and "' + checkRes.secondObj.name + '" ' + - 'with the same data source bean name "' + checkRes.firstObj.cacheStoreFactory[checkRes.firstObj.cacheStoreFactory.kind].dataSourceBean + - '" and different databases: "' + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDB) + '" in "' + checkRes.firstObj.name + '" and "' + - LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDB) + '" in "' + checkRes.secondObj.name + '" cache', - $scope.ui, 'general', 10000); + return ErrorPopover.show(ids.fieldId, 'Found ' + _objToString(checkRes.firstType, checkRes.firstObj.name) + ' with the same data source bean name "' + + checkRes.firstDs.dataSourceBean + '" and different database: "' + + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.secondDs.dialect) + '" in ' + _objToString(checkRes.secondType, checkRes.secondObj.name, 'current') + ' and "' + + LegacyUtils.cacheStoreJdbcDialectsLabel(checkRes.firstDs.dialect) + '" in ' + _objToString(checkRes.firstType, checkRes.firstObj.name), + $scope.ui, ids.section, 10000); } return true; @@ -434,6 +515,38 @@ export default ['clustersController', [ return true; } + function checkCheckpointSpis(item) { + const cfgs = item.checkpointSpi; + + if (_.isEmpty(cfgs)) + return true; + + return _.isNil(_.find(cfgs, (cfg, ix) => { + if (_.isNil(cfg.kind)) { + ErrorPopover.show('checkpointKind' + ix, 'Choose checkpoint implementation variant', $scope.ui, 'checkpoint'); + + return true; + } + + switch (cfg.kind) { + case 'Cache': + const cache = _.get(cfg, 'Cache.cache'); + + if (_.isNil(cache) || !_.find($scope.backupItem.caches, (selCache) => cache === selCache)) { + ErrorPopover.show('checkpointCacheCache' + ix, 'Choose cache from configured cluster caches', $scope.ui, 'checkpoint'); + + return true; + } + + break; + + default: break; + } + + return false; + })); + } + function checkCommunicationConfiguration(item) { const c = item.communication; @@ -467,6 +580,20 @@ export default ['clustersController', [ return true; } + function checkLoadBalancingConfiguration(item) { + const balancingSpis = item.loadBalancingSpi; + + return _.isNil(_.find(balancingSpis, (curSpi, curIx) => { + if (_.find(balancingSpis, (spi, ix) => curIx > ix && curSpi.kind === spi.kind)) { + ErrorPopover.show('loadBalancingKind' + curIx, 'Load balancing SPI of that type is already configured', $scope.ui, 'loadBalancing'); + + return true; + } + + return false; + })); + } + function checkSwapConfiguration(item) { const swapKind = item.swapSpaceSpi && item.swapSpaceSpi.kind; @@ -535,12 +662,18 @@ export default ['clustersController', [ if (!checkCacheKeyConfiguration(item)) return false; + if (!checkCheckpointSpis(item)) + return false; + if (!checkCommunicationConfiguration(item)) return false; if (!checkDiscoveryConfiguration(item)) return false; + if (!checkLoadBalancingConfiguration(item)) + return false; + if (!checkSwapConfiguration(item)) return false; @@ -564,7 +697,7 @@ export default ['clustersController', [ const idx = _.findIndex($scope.clusters, (cluster) => cluster._id === _id); if (idx >= 0) - angular.merge($scope.clusters[idx], item); + _.assign($scope.clusters[idx], item); else { item._id = _id; $scope.clusters.push(item); @@ -595,10 +728,10 @@ export default ['clustersController', [ $scope.saveItem = function() { const item = $scope.backupItem; - const swapSpi = LegacyUtils.autoClusterSwapSpiConfiguration(item, clusterCaches(item)); + const swapConfigured = item.swapSpaceSpi && item.swapSpaceSpi.kind; - if (swapSpi) - angular.extend(item, swapSpi); + if (!swapConfigured && _.find(clusterCaches(item), (cache) => cache.swapEnabled)) + _.merge(item, {swapSpaceSpi: {kind: 'FileSwapSpaceSpi'}}); if (validate(item)) save(item); http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/domains-controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/controllers/domains-controller.js b/modules/web-console/frontend/controllers/domains-controller.js index 2d450db..0a79d82 100644 --- a/modules/web-console/frontend/controllers/domains-controller.js +++ b/modules/web-console/frontend/controllers/domains-controller.js @@ -17,7 +17,7 @@ // Controller for Domain model screen. export default ['domainsController', [ - '$rootScope', '$scope', '$http', '$state', '$filter', '$timeout', '$modal', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteFocus', 'IgniteConfirm', 'IgniteConfirmBatch', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteAgentMonitor', 'IgniteLegacyTable', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'JavaTypes', 'SqlTypes', + '$rootScope', '$scope', '$http', '$state', '$filter', '$timeout', '$modal', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteFocus', 'IgniteConfirm', 'IgniteConfirmBatch', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteAgentMonitor', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', 'JavaTypes', 'SqlTypes', function($root, $scope, $http, $state, $filter, $timeout, $modal, LegacyUtils, Messages, Focus, Confirm, ConfirmBatch, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, IgniteAgentMonitor, LegacyTable, Resource, ErrorPopover, FormUtils, JavaTypes, SqlTypes) { UnsavedChangesGuard.install($scope); @@ -58,6 +58,7 @@ export default ['domainsController', [ $scope.$on('$destroy', $root.$on('user', _packageNameUpdate)); + $scope.ui.generatePojo = true; $scope.ui.builtinKeys = true; $scope.ui.usePrimitives = true; $scope.ui.generateAliases = true; @@ -75,7 +76,6 @@ export default ['domainsController', [ return !item.empty && (!item._id || _.find($scope.displayedRows, {_id: item._id})); }; - $scope.getModel = LegacyUtils.getModel; $scope.javaBuiltInClasses = LegacyUtils.javaBuiltInClasses; $scope.compactJavaName = FormUtils.compactJavaName; $scope.widthIsSufficient = FormUtils.widthIsSufficient; @@ -91,7 +91,7 @@ export default ['domainsController', [ case 'fields': case 'aliases': if (LegacyTable.tablePairSaveVisible(field, index)) - return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit); + return LegacyTable.tablePairSave($scope.tablePairValid, $scope.backupItem, field, index, stopEdit) || stopEdit; break; @@ -140,11 +140,43 @@ export default ['domainsController', [ $scope.tableEditing = LegacyTable.tableEditing; $scope.tableRemove = function(item, field, index) { - if ($scope.tableReset(true)) + if ($scope.tableReset(true)) { + // Remove field from indexes. + if (field.type === 'fields') { + _.forEach($scope.backupItem.indexes, (modelIndex) => { + modelIndex.fields = _.filter(modelIndex.fields, (indexField) => { + return indexField.name !== $scope.backupItem.fields[index].name; + }); + }); + } + LegacyTable.tableRemove(item, field, index); + } + }; + + $scope.tablePairSave = (pairValid, item, field, index, stopEdit) => { + // On change of field name update that field in index fields. + if (index >= 0 && field.type === 'fields') { + const newName = LegacyTable.tablePairValue(field, index).key; + const oldName = _.get(item, field.model)[index][field.keyName]; + + const saved = LegacyTable.tablePairSave(pairValid, item, field, index, stopEdit); + + if (saved && oldName !== newName) { + _.forEach($scope.backupItem.indexes, (idx) => { + _.forEach(idx.fields, (fld) => { + if (fld.name === oldName) + fld.name = newName; + }); + }); + } + + return saved; + } + + return LegacyTable.tablePairSave(pairValid, item, field, index, stopEdit); }; - $scope.tablePairSave = LegacyTable.tablePairSave; $scope.tablePairSaveVisible = LegacyTable.tablePairSaveVisible; $scope.queryFieldsTbl = { @@ -169,6 +201,19 @@ export default ['domainsController', [ $scope.queryMetadataVariants = LegacyUtils.mkOptions(['Annotations', 'Configuration']); + // Create list of fields to show in index fields dropdown. + $scope.fields = (prefix, cur) => { + const fields = _.map($scope.backupItem.fields, (field) => ({value: field.name, label: field.name})); + + if (prefix === 'new') + return fields; + + if (cur && !_.find(fields, {value: cur})) + fields.push({value: cur, label: cur + ' (Unknown field)'}); + + return fields; + }; + const INFO_CONNECT_TO_DB = 'Configure connection to database'; const INFO_SELECT_SCHEMAS = 'Select schemas to load tables from'; const INFO_SELECT_TABLES = 'Select tables to import as domain model'; @@ -215,6 +260,12 @@ export default ['domainsController', [ user: 'root' }, { + db: 'MySQL', + jdbcDriverClass: 'org.mariadb.jdbc.Driver', + jdbcUrl: 'jdbc:mariadb://[host]:[port]/[database]', + user: 'root' + }, + { db: 'H2', jdbcDriverClass: 'org.h2.Driver', jdbcUrl: 'jdbc:h2:tcp://[host]/[database]', @@ -223,7 +274,7 @@ export default ['domainsController', [ ]; $scope.selectedPreset = { - db: 'General', + db: 'Generic', jdbcDriverJar: '', jdbcDriverClass: '', jdbcUrl: 'jdbc:[database]', @@ -266,7 +317,7 @@ export default ['domainsController', [ const oldPreset = _.find(_dbPresets, {jdbcDriverClass: preset.jdbcDriverClass}); if (oldPreset) - angular.extend(oldPreset, preset); + _.assign(oldPreset, preset); else _dbPresets.push(preset); @@ -283,7 +334,7 @@ export default ['domainsController', [ }); if (!result) - result = {db: 'General', jdbcUrl: 'jdbc:[database]', user: 'admin'}; + result = {db: 'Generic', jdbcUrl: 'jdbc:[database]', user: 'admin'}; result.jdbcDriverJar = selectedJdbcJar.jdbcDriverJar; result.jdbcDriverClass = selectedJdbcJar.jdbcDriverClass; @@ -373,6 +424,7 @@ export default ['domainsController', [ function prepareNewItem(cacheId) { return { space: $scope.spaces[0]._id, + generatePojo: true, caches: cacheId && _.find($scope.caches, {value: cacheId}) ? [cacheId] : // eslint-disable-line no-nested-ternary (_.isEmpty($scope.caches) ? [] : [$scope.caches[0].value]), queryMetadata: 'Configuration' @@ -751,20 +803,12 @@ export default ['domainsController', [ importDomainModal.hide(); } - function _saveDomainModel() { - if (LegacyUtils.isEmptyString($scope.ui.packageName)) { - ErrorPopover.show('domainPackageNameInput', 'Package could not be empty'); - - Focus.move('domainPackageNameInput'); - - return false; - } - - if (!LegacyUtils.isValidJavaClass('Package', $scope.ui.packageName, false, 'domainPackageNameInput', true)) { - Focus.move('domainPackageNameInput'); + function _saveDomainModel(optionsForm) { + const generatePojo = $scope.ui.generatePojo; + const packageName = $scope.ui.packageName; + if (generatePojo && !LegacyUtils.checkFieldValidators({inputForm: optionsForm})) return false; - } const batch = []; const checkedCaches = []; @@ -803,7 +847,7 @@ export default ['domainsController', [ containDup = true; } - const valType = _toJavaPackage($scope.ui.packageName) + '.' + typeName; + const valType = generatePojo ? _toJavaPackage(packageName) + '.' + typeName : tableName; let _containKey = false; @@ -852,7 +896,8 @@ export default ['domainsController', [ confirm: false, skip: false, space: $scope.spaces[0], - caches: [] + caches: [], + generatePojo }; if (LegacyUtils.isDefined(domainFound)) { @@ -985,7 +1030,7 @@ export default ['domainsController', [ } } - $scope.importDomainNext = function() { + $scope.importDomainNext = function(form) { if (!$scope.importDomainNextAvailable()) return; @@ -1000,7 +1045,7 @@ export default ['domainsController', [ else if (act === 'tables') _selectOptions(); else if (act === 'options') - _saveDomainModel(); + _saveDomainModel(form); }; $scope.nextTooltipText = function() { @@ -1101,11 +1146,14 @@ export default ['domainsController', [ Resource.read() .then(({spaces, clusters, caches, domains}) => { $scope.spaces = spaces; + $scope.clusters = _.map(clusters, (cluster) => ({ label: cluster.name, value: cluster._id })); + $scope.caches = _mapCaches(caches); + $scope.domains = _.sortBy(domains, 'valueType'); _.forEach($scope.clusters, (cluster) => $scope.ui.generatedCachesClusters.push(cluster.value)); @@ -1157,8 +1205,14 @@ export default ['domainsController', [ if (form.$valid && ModelNormalizer.isEqual(__original_value, val)) form.$setPristine(); - else + else { form.$setDirty(); + + const general = form.general; + + FormUtils.markPristineInvalidAsDirty(general.keyType); + FormUtils.markPristineInvalidAsDirty(general.valueType); + } }, true); $scope.$watch('ui.activePanels.length', () => { @@ -1210,7 +1264,7 @@ export default ['domainsController', [ else $scope.backupItem = emptyDomain; - $scope.backupItem = angular.merge({}, blank, $scope.backupItem); + $scope.backupItem = _.merge({}, blank, $scope.backupItem); if ($scope.ui.inputForm) { $scope.ui.inputForm.$error = {}; @@ -1252,9 +1306,12 @@ export default ['domainsController', [ const indexes = item.indexes; if (indexes && indexes.length > 0) { - if (_.find(indexes, function(index, i) { + if (_.find(indexes, function(index, idx) { if (_.isEmpty(index.fields)) - return !ErrorPopover.show('indexes' + i, 'Index fields are not specified', $scope.ui, 'query'); + return !ErrorPopover.show('indexes' + idx, 'Index fields are not specified', $scope.ui, 'query'); + + if (_.find(index.fields, (field) => !_.find(item.fields, (configuredField) => configuredField.name === field.name))) + return !ErrorPopover.show('indexes' + idx, 'Index contains not configured fields', $scope.ui, 'query'); })) return false; } @@ -1332,7 +1389,7 @@ export default ['domainsController', [ }); if (idx >= 0) - angular.extend($scope.domains[idx], savedMeta); + _.assign($scope.domains[idx], savedMeta); else $scope.domains.push(savedMeta); @@ -1503,15 +1560,11 @@ export default ['domainsController', [ }); // Found duplicate by key. - if (idx >= 0 && idx !== index) { - if (stopEdit) - return false; - - return ErrorPopover.show(LegacyTable.tableFieldId(index, pairField.idPrefix + pairField.id), 'Field with such ' + pairField.dupObjName + ' already exists!', $scope.ui, 'query'); - } + if (idx >= 0 && idx !== index) + return !stopEdit && ErrorPopover.show(LegacyTable.tableFieldId(index, pairField.idPrefix + pairField.id), 'Field with such ' + pairField.dupObjName + ' already exists!', $scope.ui, 'query'); } - if (pairField.classValidation && !LegacyUtils.isValidJavaClass(pairField.msg, pairValue.value, true, LegacyTable.tableFieldId(index, 'Value' + pairField.id), false, $scope.ui, 'query')) { + if (pairField.classValidation && !LegacyUtils.isValidJavaClass(pairField.msg, pairValue.value, true, LegacyTable.tableFieldId(index, 'Value' + pairField.id), false, $scope.ui, 'query', stopEdit)) { if (stopEdit) return false; @@ -1560,8 +1613,8 @@ export default ['domainsController', [ let model = item[field.model]; - if (!LegacyUtils.isValidJavaIdentifier(dbFieldTable.msg + ' java name', dbFieldValue.javaFieldName, LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id))) - return false; + if (!LegacyUtils.isValidJavaIdentifier(dbFieldTable.msg + ' java name', dbFieldValue.javaFieldName, LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), $scope.ui, 'store', stopEdit)) + return stopEdit; if (LegacyUtils.isDefined(model)) { let idx = _.findIndex(model, function(dbMeta) { @@ -1570,7 +1623,7 @@ export default ['domainsController', [ // Found duplicate. if (idx >= 0 && index !== idx) - return ErrorPopover.show(LegacyTable.tableFieldId(index, 'DatabaseFieldName' + dbFieldTable.id), 'Field with such database name already exists!', $scope.ui, 'store'); + return stopEdit || ErrorPopover.show(LegacyTable.tableFieldId(index, 'DatabaseFieldName' + dbFieldTable.id), 'Field with such database name already exists!', $scope.ui, 'store'); idx = _.findIndex(model, function(dbMeta) { return dbMeta.javaFieldName === dbFieldValue.javaFieldName; @@ -1578,7 +1631,7 @@ export default ['domainsController', [ // Found duplicate. if (idx >= 0 && index !== idx) - return ErrorPopover.show(LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), 'Field with such java name already exists!', $scope.ui, 'store'); + return stopEdit || ErrorPopover.show(LegacyTable.tableFieldId(index, 'JavaFieldName' + dbFieldTable.id), 'Field with such java name already exists!', $scope.ui, 'store'); if (index < 0) model.push(dbFieldValue); @@ -1639,7 +1692,7 @@ export default ['domainsController', [ // Found duplicate. if (idx >= 0 && idx !== curIdx) - return ErrorPopover.show(LegacyTable.tableFieldId(curIdx, 'IndexName'), 'Index with such name already exists!', $scope.ui, 'query'); + return !stopEdit && ErrorPopover.show(LegacyTable.tableFieldId(curIdx, 'IndexName'), 'Index with such name already exists!', $scope.ui, 'query'); } LegacyTable.tableReset(); @@ -1675,10 +1728,8 @@ export default ['domainsController', [ $scope.tableIndexNewItem = function(field, indexIdx) { if ($scope.tableReset(true)) { - const index = $scope.backupItem.indexes[indexIdx]; - LegacyTable.tableState(field, -1, 'table-index-fields'); - LegacyTable.tableFocusInvalidField(-1, 'FieldName' + (index.indexType === 'SORTED' ? 'S' : '') + indexIdx); + LegacyTable.tableFocusInvalidField(-1, 'FieldName' + indexIdx); field.newFieldName = null; field.newDirection = true; @@ -1734,7 +1785,7 @@ export default ['domainsController', [ field.curDirection = indexItem.direction; field.indexIdx = indexIdx; - Focus.move('curFieldName' + (index.indexType === 'SORTED' ? 'S' : '') + field.indexIdx + '-' + curIdx); + Focus.move('curFieldName' + field.indexIdx + '-' + curIdx); } }; @@ -1753,8 +1804,11 @@ export default ['domainsController', [ const idx = _.findIndex(fields, (fld) => fld.name === indexItemValue.name); // Found duplicate. - if (idx >= 0 && idx !== curIdx) - return ErrorPopover.show(LegacyTable.tableFieldId(curIdx, 'FieldName' + (index.indexType === 'SORTED' ? 'S' : '') + indexIdx + (curIdx >= 0 ? '-' : '')), 'Field with such name already exists in index!', $scope.ui, 'query'); + if (idx >= 0 && idx !== curIdx) { + return !stopEdit && ErrorPopover.show(LegacyTable.tableFieldId(curIdx, + 'FieldName' + indexIdx + (curIdx >= 0 ? '-' : '')), + 'Field with such name already exists in index!', $scope.ui, 'query'); + } } LegacyTable.tableReset(); http://git-wip-us.apache.org/repos/asf/ignite/blob/087f6405/modules/web-console/frontend/controllers/igfs-controller.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/controllers/igfs-controller.js b/modules/web-console/frontend/controllers/igfs-controller.js index 7617712..e505f1c 100644 --- a/modules/web-console/frontend/controllers/igfs-controller.js +++ b/modules/web-console/frontend/controllers/igfs-controller.js @@ -17,7 +17,7 @@ // Controller for IGFS screen. export default ['igfsController', [ - '$scope', '$http', '$state', '$filter', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteLegacyTable', 'igniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', + '$scope', '$http', '$state', '$filter', '$timeout', 'IgniteLegacyUtils', 'IgniteMessages', 'IgniteConfirm', 'IgniteClone', 'IgniteLoading', 'IgniteModelNormalizer', 'IgniteUnsavedChangesGuard', 'IgniteLegacyTable', 'IgniteConfigurationResource', 'IgniteErrorPopover', 'IgniteFormUtils', function($scope, $http, $state, $filter, $timeout, LegacyUtils, Messages, Confirm, Clone, Loading, ModelNormalizer, UnsavedChangesGuard, LegacyTable, Resource, ErrorPopover, FormUtils) { UnsavedChangesGuard.install($scope); @@ -231,7 +231,7 @@ export default ['igfsController', [ else $scope.backupItem = emptyIgfs; - $scope.backupItem = angular.merge({}, blank, $scope.backupItem); + $scope.backupItem = _.merge({}, blank, $scope.backupItem); if ($scope.ui.inputForm) { $scope.ui.inputForm.$error = {}; @@ -304,7 +304,7 @@ export default ['igfsController', [ }); if (idx >= 0) - angular.merge($scope.igfss[idx], item); + _.assign($scope.igfss[idx], item); else { item._id = _id; $scope.igfss.push(item);
