http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-download-project/index.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-download-project/index.js b/modules/web-console/frontend/app/components/page-configure/components/button-download-project/index.js new file mode 100644 index 0000000..4a220db --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-download-project/index.js @@ -0,0 +1,23 @@ +/* + * 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 angular from 'angular'; +import {component} from './component'; + +export default angular +.module('configuration.button-download-project', []) +.component(component.name, component);
http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-download-project/template.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-download-project/template.pug b/modules/web-console/frontend/app/components/page-configure/components/button-download-project/template.pug new file mode 100644 index 0000000..0264676 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-download-project/template.pug @@ -0,0 +1,22 @@ +//- + 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. + +button.btn-ignite.btn-ignite--success( + type='button' + ng-click='$ctrl.download()' +) + svg(ignite-icon='download').icon-left + | Download project http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-import-models/component.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-import-models/component.js b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/component.js new file mode 100644 index 0000000..28b7aa0 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/component.js @@ -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. + */ + +import template from './template.pug'; +import './style.scss'; + +export class ButtonImportModels { + static $inject = ['ModalImportModels']; + constructor(ModalImportModels) { + Object.assign(this, {ModalImportModels}); + } + startImport() { + return this.ModalImportModels.open(this.clusterID); + } +} +export const component = { + name: 'buttonImportModels', + controller: ButtonImportModels, + template, + bindings: { + clusterID: '<clusterId' + } +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-import-models/index.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-import-models/index.js b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/index.js new file mode 100644 index 0000000..b7ef527 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/index.js @@ -0,0 +1,23 @@ +/* + * 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 angular from 'angular'; +import {component} from './component'; + +export default angular +.module('configuration.button-import-models', []) +.component(component.name, component); http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-import-models/style.scss ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-import-models/style.scss b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/style.scss new file mode 100644 index 0000000..4944626 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/style.scss @@ -0,0 +1,25 @@ +/* + * 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. + */ + +button-import-models { + display: inline-block; + + button { + // Ensures same height for wrapper element and button + vertical-align: top; + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-import-models/template.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-import-models/template.pug b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/template.pug new file mode 100644 index 0000000..25c3531 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-import-models/template.pug @@ -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. + +button.btn-ignite.btn-ignite--primary( + ng-click='$ctrl.startImport()' + type='button' +) Import from Database \ 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/components/button-preview-project/component.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/component.js b/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/component.js new file mode 100644 index 0000000..095cb0f --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/component.js @@ -0,0 +1,36 @@ +/* + * 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 template from './template.pug'; + +export class ButtonPreviewProject { + static $inject = ['ModalPreviewProject']; + constructor(ModalPreviewProject) { + Object.assign(this, {ModalPreviewProject}); + } + preview() { + return this.ModalPreviewProject.open(this.cluster); + } +} +export const component = { + name: 'buttonPreviewProject', + controller: ButtonPreviewProject, + template, + bindings: { + cluster: '<' + } +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/index.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/index.js b/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/index.js new file mode 100644 index 0000000..d5f6191 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/index.js @@ -0,0 +1,23 @@ +/* + * 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 angular from 'angular'; +import {component} from './component'; + +export default angular +.module('configuration.button-preview-project', []) +.component(component.name, component); http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/template.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/template.pug b/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/template.pug new file mode 100644 index 0000000..3c3ca38 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/button-preview-project/template.pug @@ -0,0 +1,22 @@ +//- + 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. + +button.btn-ignite.btn-ignite--link-dashed-success( + type='button' + ng-click='$ctrl.preview()' +) + svg(ignite-icon='structure').icon-left + | See project structure \ 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/components/fakeUICanExit.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/fakeUICanExit.js b/modules/web-console/frontend/app/components/page-configure/components/fakeUICanExit.js new file mode 100644 index 0000000..c0837ed --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/fakeUICanExit.js @@ -0,0 +1,48 @@ +/* + * 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. + */ + +class FakeUiCanExitController { + static $inject = ['$element', '$transitions']; + static CALLBACK_NAME = 'uiCanExit'; + constructor($element, $transitions) { + Object.assign(this, {$element, $transitions}); + } + $onInit() { + const data = this.$element.data(); + const {CALLBACK_NAME} = this.constructor; + const controllerWithCallback = Object.keys(data) + .map((key) => data[key]) + .find((controller) => controller[CALLBACK_NAME]); + if (!controllerWithCallback) return; + const off = this.$transitions.onBefore({from: this.fromState}, (...args) => { + return controllerWithCallback[CALLBACK_NAME](...args); + }); + } + $onDestroy() { + if (this.off) this.off(); + this.$element = null; + } +} + +export default function fakeUiCanExit() { + return { + bindToController: { + fromState: '@fakeUiCanExit' + }, + controller: FakeUiCanExitController + }; +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/formUICanExitGuard.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/formUICanExitGuard.js b/modules/web-console/frontend/app/components/page-configure/components/formUICanExitGuard.js new file mode 100644 index 0000000..0225c5f --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/formUICanExitGuard.js @@ -0,0 +1,59 @@ +/* + * 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 {default as ConfigChangesGuard} from '../services/ConfigChangesGuard'; + +class FormUICanExitGuardController { + static $inject = ['$element', ConfigChangesGuard.name]; + /** + * @param {JQLite} $element + * @param {ConfigChangesGuard} ConfigChangesGuard + */ + constructor($element, ConfigChangesGuard) { + this.$element = $element; + this.ConfigChangesGuard = ConfigChangesGuard; + } + $onDestroy() { + this.$element = null; + } + $onInit() { + const data = this.$element.data(); + const controller = Object.keys(data) + .map((key) => data[key]) + .find(this._itQuacks); + + if (!controller) return; + + controller.uiCanExit = ($transition$) => { + if ($transition$.options().custom.justIDUpdate) return true; + $transition$.onSuccess({}, controller.reset); + return this.ConfigChangesGuard.guard(...controller.getValuesToCompare()); + }; + } + _itQuacks(controller) { + return controller.reset instanceof Function && + controller.getValuesToCompare instanceof Function && + !controller.uiCanExit; + } +} + +export default function formUiCanExitGuard() { + return { + priority: 10, + controller: FormUICanExitGuardController + }; +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/component.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/component.js b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/component.js new file mode 100644 index 0000000..7f852b0 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/component.js @@ -0,0 +1,1151 @@ +/* + * 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 templateUrl from './template.tpl.pug'; +import './style.scss'; +import _ from 'lodash'; +import naturalCompare from 'natural-compare-lite'; +import find from 'lodash/fp/find'; +import get from 'lodash/fp/get'; +import {Observable} from 'rxjs/Observable'; +import ObjectID from 'bson-objectid'; +import {uniqueName} from 'app/utils/uniqueName'; +import {defaultNames} from '../../defaultNames'; + +// eslint-disable-next-line +import {UIRouter} from '@uirouter/angularjs' +import {default as IgniteConfirmBatch} from 'app/services/ConfirmBatch.service'; +import {default as ConfigSelectors} from 'app/components/page-configure/store/selectors'; +import {default as ConfigEffects} from 'app/components/page-configure/store/effects'; +import {default as ConfigureState} from 'app/components/page-configure/services/ConfigureState'; +// eslint-disable-next-line +import {default as AgentManager} from 'app/modules/agent/AgentModal.service' +import {default as SqlTypes} from 'app/services/SqlTypes.service'; +import {default as JavaTypes} from 'app/services/JavaTypes.service'; +// eslint-disable-next-line +import {default as ActivitiesData} from 'app/core/activities/Activities.data'; + +function _mapCaches(caches = []) { + return caches.map((cache) => { + return {label: cache.name, value: cache._id, cache}; + }); +} + +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'; +const INFO_SELECT_OPTIONS = 'Select import domain model options'; +const LOADING_JDBC_DRIVERS = {text: 'Loading JDBC drivers...'}; +const LOADING_SCHEMAS = {text: 'Loading schemas...'}; +const LOADING_TABLES = {text: 'Loading tables...'}; +const SAVING_DOMAINS = {text: 'Saving domain model...'}; + +const IMPORT_DM_NEW_CACHE = 1; +const IMPORT_DM_ASSOCIATE_CACHE = 2; + +const DFLT_PARTITIONED_CACHE = { + label: 'PARTITIONED', + value: -1, + cache: { + name: 'PARTITIONED', + cacheMode: 'PARTITIONED', + atomicityMode: 'ATOMIC', + readThrough: true, + writeThrough: true + } +}; + +const DFLT_REPLICATED_CACHE = { + label: 'REPLICATED', + value: -2, + cache: { + name: 'REPLICATED', + cacheMode: 'REPLICATED', + atomicityMode: 'ATOMIC', + readThrough: true, + writeThrough: true + } +}; + +const CACHE_TEMPLATES = [DFLT_PARTITIONED_CACHE, DFLT_REPLICATED_CACHE]; + +export class ModalImportModels { + /** + * Cluster ID to import models into + * @type {string} + */ + clusterID; + + /** @type {ng.ICompiledExpression} */ + onHide; + + static $inject = ['$uiRouter', ConfigSelectors.name, ConfigEffects.name, ConfigureState.name, '$http', 'IgniteConfirm', IgniteConfirmBatch.name, 'IgniteFocus', SqlTypes.name, JavaTypes.name, 'IgniteMessages', '$scope', '$rootScope', 'AgentManager', 'IgniteActivitiesData', 'IgniteLoading', 'IgniteFormUtils', 'IgniteLegacyUtils']; + /** + * @param {UIRouter} $uiRouter + * @param {ConfigSelectors} ConfigSelectors + * @param {ConfigEffects} ConfigEffects + * @param {ConfigureState} ConfigureState + * @param {ng.IHttpService} $http + * @param {IgniteConfirmBatch} ConfirmBatch + * @param {SqlTypes} SqlTypes + * @param {JavaTypes} JavaTypes + * @param {ng.IScope} $scope + * @param {ng.IRootScopeService} $root + * @param {AgentManager} agentMgr + * @param {ActivitiesData} ActivitiesData + */ + constructor($uiRouter, ConfigSelectors, ConfigEffects, ConfigureState, $http, Confirm, ConfirmBatch, Focus, SqlTypes, JavaTypes, Messages, $scope, $root, agentMgr, ActivitiesData, Loading, FormUtils, LegacyUtils) { + this.$uiRouter = $uiRouter; + this.ConfirmBatch = ConfirmBatch; + this.$http = $http; + this.ConfigSelectors = ConfigSelectors; + this.ConfigEffects = ConfigEffects; + this.ConfigureState = ConfigureState; + this.$root = $root; + this.$scope = $scope; + this.agentMgr = agentMgr; + this.JavaTypes = JavaTypes; + this.SqlTypes = SqlTypes; + this.ActivitiesData = ActivitiesData; + Object.assign(this, {Confirm, Focus, Messages, Loading, FormUtils, LegacyUtils}); + } + loadData() { + return Observable.of(this.clusterID) + .switchMap((id = 'new') => { + return this.ConfigureState.state$.let(this.ConfigSelectors.selectClusterToEdit(id, defaultNames.importedCluster)); + }) + .switchMap((cluster) => { + return (!(cluster.caches || []).length && !(cluster.models || []).length) + ? Observable.of({ + cluster, + caches: [], + models: [] + }) + : Observable.fromPromise(Promise.all([ + this.ConfigEffects.etp('LOAD_SHORT_CACHES', {ids: cluster.caches || [], clusterID: cluster._id}), + this.ConfigEffects.etp('LOAD_SHORT_MODELS', {ids: cluster.models || [], clusterID: cluster._id}) + ])) + .switchMap(() => { + return Observable.combineLatest( + this.ConfigureState.state$.let(this.ConfigSelectors.selectShortCachesValue()), + this.ConfigureState.state$.let(this.ConfigSelectors.selectShortModelsValue()), + (caches, models) => ({ + cluster, + caches, + models + }) + ).take(1); + }); + }) + .take(1); + } + saveBatch(batch) { + if (!batch.length) return; + this.Loading.start('importDomainFromDb'); + this.ConfigureState.dispatchAction({ + type: 'ADVANCED_SAVE_COMPLETE_CONFIGURATION', + changedItems: this.batchActionsToRequestBody(batch), + prevActions: [] + }); + this.saveSubscription = Observable.race( + this.ConfigureState.actions$.filter((a) => a.type === 'ADVANCED_SAVE_COMPLETE_CONFIGURATION_OK') + .do(() => this.onHide()), + this.ConfigureState.actions$.filter((a) => a.type === 'ADVANCED_SAVE_COMPLETE_CONFIGURATION_ERR') + ) + .take(1) + .do(() => { + this.Loading.finish('importDomainFromDb'); + }) + .subscribe(); + } + batchActionsToRequestBody(batch) { + const result = batch.reduce((req, action) => { + return { + ...req, + cluster: { + ...req.cluster, + models: [...req.cluster.models, action.newDomainModel._id], + caches: [...req.cluster.caches, ...action.newDomainModel.caches] + }, + models: [...req.models, action.newDomainModel], + caches: action.newCache + ? [...req.caches, action.newCache] + : action.cacheStoreChanges + ? [...req.caches, { + ...this.loadedCaches[action.cacheStoreChanges[0].cacheId], + ...action.cacheStoreChanges[0].change + }] + : req.caches + }; + }, {cluster: this.cluster, models: [], caches: [], igfss: []}); + result.cluster.models = [...new Set(result.cluster.models)]; + result.cluster.caches = [...new Set(result.cluster.caches)]; + return result; + } + onTableSelectionChange(selected) { + this.$scope.$applyAsync(() => { + this.$scope.importDomain.tablesToUse = selected; + this.selectedTablesIDs = selected.map((t) => t.id); + }); + } + onSchemaSelectionChange(selected) { + this.$scope.$applyAsync(() => { + this.$scope.importDomain.schemasToUse = selected; + this.selectedSchemasIDs = selected.map((i) => i.name); + }); + } + onVisibleRowsChange(rows) { + return this.visibleTables = rows.map((r) => r.entity); + } + onCacheSelect(cacheID) { + if (cacheID < 0) return; + if (this.loadedCaches[cacheID]) return; + return this.onCacheSelectSubcription = Observable.merge( + Observable.timer(0, 1).take(1) + .do(() => this.ConfigureState.dispatchAction({type: 'LOAD_CACHE', cacheID})), + Observable.race( + this.ConfigureState.actions$ + .filter((a) => a.type === 'LOAD_CACHE_OK' && a.cache._id === cacheID).pluck('cache') + .do((cache) => { + this.loadedCaches[cacheID] = cache; + }), + this.ConfigureState.actions$ + .filter((a) => a.type === 'LOAD_CACHE_ERR' && a.action.cacheID === cacheID) + ).take(1) + ) + .subscribe(); + } + $onDestroy() { + this.subscription.unsubscribe(); + if (this.onCacheSelectSubcription) this.onCacheSelectSubcription.unsubscribe(); + if (this.saveSubscription) this.saveSubscription.unsubscribe(); + } + $onInit() { + // Restores old behavior + const {$http, Confirm, ConfirmBatch, Focus, SqlTypes, JavaTypes, Messages, $scope, $root, agentMgr, ActivitiesData, Loading, FormUtils, LegacyUtils} = this; + + /** + * Convert some name to valid java package name. + * + * @param name to convert. + * @returns {string} Valid java package name. + */ + const _toJavaPackage = (name) => { + return name ? name.replace(/[^A-Za-z_0-9/.]+/g, '_') : 'org'; + }; + + const importDomainModal = { + hide: () => { + agentMgr.stopWatch(); + this.onHide(); + } + }; + + const _makeDefaultPackageName = (user) => user + ? _toJavaPackage(`${user.email.replace('@', '.').split('.').reverse().join('.')}.model`) + : void 0; + + this.$scope.ui = { + generatePojo: true, + builtinKeys: true, + generateKeyFields: true, + usePrimitives: true, + generateTypeAliases: true, + generateFieldAliases: true, + packageNameUserInput: _makeDefaultPackageName($root.user) + }; + this.$scope.$hide = importDomainModal.hide; + + this.$scope.importCommon = {}; + + this.subscription = this.loadData().do((data) => { + this.$scope.caches = _mapCaches(data.caches); + this.$scope.domains = data.models; + this.caches = data.caches; + this.cluster = data.cluster; + + if (!_.isEmpty(this.$scope.caches)) { + this.$scope.importActions.push({ + label: 'Associate with existing cache', + shortLabel: 'Associate', + value: IMPORT_DM_ASSOCIATE_CACHE + }); + } + this.$scope.$watch('importCommon.action', this._fillCommonCachesOrTemplates(this.$scope.importCommon), true); + this.$scope.importCommon.action = IMPORT_DM_NEW_CACHE; + }).subscribe(); + + // New + + this.loadedCaches = { + ...CACHE_TEMPLATES.reduce((a, c) => ({...a, [c.value]: c.cache}), {}) + }; + this.actions = [ + {value: 'connect', label: this.$root.IgniteDemoMode ? 'Description' : 'Connection'}, + {value: 'schemas', label: 'Schemas'}, + {value: 'tables', label: 'Tables'}, + {value: 'options', label: 'Options'} + ]; + + // Legacy + + $scope.ui.invalidKeyFieldsTooltip = 'Found key types without configured key fields<br/>' + + 'It may be a result of import tables from database without primary keys<br/>' + + 'Key field for such key types should be configured manually'; + + $scope.indexType = LegacyUtils.mkOptions(['SORTED', 'FULLTEXT', 'GEOSPATIAL']); + + $scope.importActions = [{ + label: 'Create new cache by template', + shortLabel: 'Create', + value: IMPORT_DM_NEW_CACHE + }]; + + + const _dbPresets = [ + { + db: 'Oracle', + jdbcDriverClass: 'oracle.jdbc.OracleDriver', + jdbcUrl: 'jdbc:oracle:thin:@[host]:[port]:[database]', + user: 'system' + }, + { + db: 'DB2', + jdbcDriverClass: 'com.ibm.db2.jcc.DB2Driver', + jdbcUrl: 'jdbc:db2://[host]:[port]/[database]', + user: 'db2admin' + }, + { + db: 'SQLServer', + jdbcDriverClass: 'com.microsoft.sqlserver.jdbc.SQLServerDriver', + jdbcUrl: 'jdbc:sqlserver://[host]:[port][;databaseName=database]' + }, + { + db: 'PostgreSQL', + jdbcDriverClass: 'org.postgresql.Driver', + jdbcUrl: 'jdbc:postgresql://[host]:[port]/[database]', + user: 'sa' + }, + { + db: 'MySQL', + jdbcDriverClass: 'com.mysql.jdbc.Driver', + jdbcUrl: 'jdbc:mysql://[host]:[port]/[database]', + 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]', + user: 'sa' + } + ]; + + $scope.selectedPreset = { + db: 'Generic', + jdbcDriverJar: '', + jdbcDriverClass: '', + jdbcUrl: 'jdbc:[database]', + user: 'sa', + password: '', + tablesOnly: true + }; + + $scope.demoConnection = { + db: 'H2', + jdbcDriverClass: 'org.h2.Driver', + jdbcUrl: 'jdbc:h2:mem:demo-db', + user: 'sa', + password: '', + tablesOnly: true + }; + + function _loadPresets() { + try { + const restoredPresets = JSON.parse(localStorage.dbPresets); + + _.forEach(restoredPresets, (restoredPreset) => { + const preset = _.find(_dbPresets, {jdbcDriverClass: restoredPreset.jdbcDriverClass}); + + if (preset) { + preset.jdbcUrl = restoredPreset.jdbcUrl; + preset.user = restoredPreset.user; + } + }); + } + catch (ignore) { + // No-op. + } + } + + _loadPresets(); + + function _savePreset(preset) { + try { + const oldPreset = _.find(_dbPresets, {jdbcDriverClass: preset.jdbcDriverClass}); + + if (oldPreset) + _.assign(oldPreset, preset); + else + _dbPresets.push(preset); + + localStorage.dbPresets = JSON.stringify(_dbPresets); + } + catch (err) { + Messages.showError(err); + } + } + + function _findPreset(selectedJdbcJar) { + let result = _.find(_dbPresets, function(preset) { + return preset.jdbcDriverClass === selectedJdbcJar.jdbcDriverClass; + }); + + if (!result) + result = {db: 'Generic', jdbcUrl: 'jdbc:[database]', user: 'admin'}; + + result.jdbcDriverJar = selectedJdbcJar.jdbcDriverJar; + result.jdbcDriverClass = selectedJdbcJar.jdbcDriverClass; + + return result; + } + + function isValidJavaIdentifier(s) { + return JavaTypes.validIdentifier(s) && !JavaTypes.isKeyword(s) && JavaTypes.nonBuiltInClass(s) && + SqlTypes.validIdentifier(s) && !SqlTypes.isKeyword(s); + } + + function toJavaIdentifier(name) { + if (_.isEmpty(name)) + return 'DB'; + + const len = name.length; + + let ident = ''; + + let capitalizeNext = true; + + for (let i = 0; i < len; i++) { + const ch = name.charAt(i); + + if (ch === ' ' || ch === '_') + capitalizeNext = true; + else if (ch === '-') { + ident += '_'; + capitalizeNext = true; + } + else if (capitalizeNext) { + ident += ch.toLocaleUpperCase(); + + capitalizeNext = false; + } + else + ident += ch.toLocaleLowerCase(); + } + + return ident; + } + + function toJavaClassName(name) { + const clazzName = toJavaIdentifier(name); + + if (isValidJavaIdentifier(clazzName)) + return clazzName; + + return 'Class' + clazzName; + } + + function toJavaFieldName(dbName) { + const javaName = toJavaIdentifier(dbName); + + const fieldName = javaName.charAt(0).toLocaleLowerCase() + javaName.slice(1); + + if (isValidJavaIdentifier(fieldName)) + return fieldName; + + return 'field' + javaName; + } + + /** + * Load list of database schemas. + */ + const _loadSchemas = () => { + agentMgr.awaitAgent() + .then(function() { + $scope.importDomain.loadingOptions = LOADING_SCHEMAS; + Loading.start('importDomainFromDb'); + + if ($root.IgniteDemoMode) + return agentMgr.schemas($scope.demoConnection); + + const preset = $scope.selectedPreset; + + _savePreset(preset); + + return agentMgr.schemas(preset); + }) + .then((schemaInfo) => { + $scope.importDomain.action = 'schemas'; + $scope.importDomain.info = INFO_SELECT_SCHEMAS; + $scope.importDomain.catalog = toJavaIdentifier(schemaInfo.catalog); + $scope.importDomain.schemas = _.map(schemaInfo.schemas, (schema) => ({name: schema})); + $scope.importDomain.schemasToUse = $scope.importDomain.schemas; + this.selectedSchemasIDs = $scope.importDomain.schemas.map((s) => s.name); + + if ($scope.importDomain.schemas.length === 0) + $scope.importDomainNext(); + }) + .catch(Messages.showError) + .then(() => Loading.finish('importDomainFromDb')); + }; + + + this._importCachesOrTemplates = []; + + $scope.tableActionView = (tbl) => { + const cacheName = get('label')(find({value: tbl.cacheOrTemplate})); + + if (tbl.action === IMPORT_DM_NEW_CACHE) + return 'Create ' + tbl.generatedCacheName + ' (' + cacheName + ')'; + + return 'Associate with ' + cacheName; + }; + + /** + * Load list of database tables. + */ + const _loadTables = () => { + agentMgr.awaitAgent() + .then(() => { + $scope.importDomain.loadingOptions = LOADING_TABLES; + Loading.start('importDomainFromDb'); + + $scope.importDomain.allTablesSelected = false; + this.selectedTables = []; + + const preset = $scope.importDomain.demo ? $scope.demoConnection : $scope.selectedPreset; + + preset.schemas = $scope.importDomain.schemasToUse.map((s) => s.name); + + return agentMgr.tables(preset); + }) + .then((tables) => { + this._importCachesOrTemplates = CACHE_TEMPLATES.concat($scope.caches); + + this._fillCommonCachesOrTemplates($scope.importCommon)($scope.importCommon.action); + + _.forEach(tables, (tbl, idx) => { + tbl.id = idx; + tbl.action = IMPORT_DM_NEW_CACHE; + // tbl.generatedCacheName = toJavaClassName(tbl.table) + 'Cache'; + tbl.generatedCacheName = uniqueName(toJavaClassName(tbl.table) + 'Cache', this.caches); + tbl.cacheOrTemplate = DFLT_PARTITIONED_CACHE.value; + tbl.label = tbl.schema + '.' + tbl.table; + tbl.edit = false; + }); + + $scope.importDomain.action = 'tables'; + $scope.importDomain.tables = tables; + const tablesToUse = tables.filter((t) => LegacyUtils.isDefined(_.find(t.columns, (col) => col.key))); + this.selectedTablesIDs = tablesToUse.map((t) => t.id); + this.$scope.importDomain.tablesToUse = tablesToUse; + + $scope.importDomain.info = INFO_SELECT_TABLES; + }) + .catch(Messages.showError) + .then(() => Loading.finish('importDomainFromDb')); + }; + + $scope.applyDefaults = () => { + _.forEach(this.visibleTables, (table) => { + table.edit = false; + table.action = $scope.importCommon.action; + table.cacheOrTemplate = $scope.importCommon.cacheOrTemplate; + }); + }; + + $scope._curDbTable = null; + + $scope.startEditDbTableCache = (tbl) => { + if ($scope._curDbTable) { + $scope._curDbTable.edit = false; + + if ($scope._curDbTable.actionWatch) { + $scope._curDbTable.actionWatch(); + + $scope._curDbTable.actionWatch = null; + } + } + + $scope._curDbTable = tbl; + + const _fillFn = this._fillCommonCachesOrTemplates($scope._curDbTable); + + _fillFn($scope._curDbTable.action); + + $scope._curDbTable.actionWatch = $scope.$watch('_curDbTable.action', _fillFn, true); + + $scope._curDbTable.edit = true; + }; + + /** + * Show page with import domain models options. + */ + function _selectOptions() { + $scope.importDomain.action = 'options'; + $scope.importDomain.button = 'Save'; + $scope.importDomain.info = INFO_SELECT_OPTIONS; + + Focus.move('domainPackageName'); + } + + const _saveDomainModel = (optionsForm) => { + if (optionsForm.$invalid) + return this.FormUtils.triggerValidation(optionsForm, this.$scope); + + const generatePojo = $scope.ui.generatePojo; + const packageName = $scope.ui.packageName; + + const batch = []; + const checkedCaches = []; + + let containKey = true; + let containDup = false; + + function dbField(name, jdbcType, nullable, unsigned) { + const javaTypes = (unsigned && jdbcType.unsigned) ? jdbcType.unsigned : jdbcType.signed; + const javaFieldType = (!nullable && javaTypes.primitiveType && $scope.ui.usePrimitives) ? javaTypes.primitiveType : javaTypes.javaType; + + return { + databaseFieldName: name, + databaseFieldType: jdbcType.dbName, + javaType: javaTypes.javaType, + javaFieldName: toJavaFieldName(name), + javaFieldType + }; + } + + _.forEach($scope.importDomain.tablesToUse, (table, curIx, tablesToUse) => { + const qryFields = []; + const indexes = []; + const keyFields = []; + const valFields = []; + const aliases = []; + + const tableName = table.table; + let typeName = toJavaClassName(tableName); + + if (_.find($scope.importDomain.tablesToUse, + (tbl, ix) => ix !== curIx && tableName === tbl.table)) { + typeName = typeName + '_' + toJavaClassName(table.schema); + + containDup = true; + } + + let valType = tableName; + let typeAlias; + + if (generatePojo) { + if ($scope.ui.generateTypeAliases && tableName.toLowerCase() !== typeName.toLowerCase()) + typeAlias = tableName; + + valType = _toJavaPackage(packageName) + '.' + typeName; + } + + let _containKey = false; + + _.forEach(table.columns, function(col) { + const fld = dbField(col.name, SqlTypes.findJdbcType(col.type), col.nullable, col.unsigned); + + qryFields.push({name: fld.javaFieldName, className: fld.javaType}); + + const dbName = fld.databaseFieldName; + + if (generatePojo && $scope.ui.generateFieldAliases && + SqlTypes.validIdentifier(dbName) && !SqlTypes.isKeyword(dbName) && + !_.find(aliases, {field: fld.javaFieldName}) && + fld.javaFieldName.toUpperCase() !== dbName.toUpperCase()) + aliases.push({field: fld.javaFieldName, alias: dbName}); + + if (col.key) { + keyFields.push(fld); + + _containKey = true; + } + else + valFields.push(fld); + }); + + containKey &= _containKey; + if (table.indexes) { + _.forEach(table.indexes, (idx) => { + const idxFields = _.map(idx.fields, (idxFld) => ({ + name: toJavaFieldName(idxFld.name), + direction: idxFld.sortOrder + })); + + indexes.push({ + name: idx.name, + indexType: 'SORTED', + fields: idxFields + }); + }); + } + + const domainFound = _.find($scope.domains, (domain) => domain.valueType === valType); + + const batchAction = { + confirm: false, + skip: false, + table, + newDomainModel: { + _id: ObjectID.generate(), + caches: [], + generatePojo + } + }; + + if (LegacyUtils.isDefined(domainFound)) { + batchAction.newDomainModel._id = domainFound._id; + // Don't touch original caches value + delete batchAction.newDomainModel.caches; + batchAction.confirm = true; + } + + Object.assign(batchAction.newDomainModel, { + tableName: typeAlias, + keyType: valType + 'Key', + valueType: valType, + queryMetadata: 'Configuration', + databaseSchema: table.schema, + databaseTable: tableName, + fields: qryFields, + queryKeyFields: _.map(keyFields, (field) => field.javaFieldName), + indexes, + keyFields, + aliases, + valueFields: _.isEmpty(valFields) ? keyFields.slice() : valFields + }); + + // Use Java built-in type for key. + if ($scope.ui.builtinKeys && batchAction.newDomainModel.keyFields.length === 1) { + const newDomain = batchAction.newDomainModel; + const keyField = newDomain.keyFields[0]; + + newDomain.keyType = keyField.javaType; + newDomain.keyFieldName = keyField.javaFieldName; + + if (!$scope.ui.generateKeyFields) { + // Exclude key column from query fields. + newDomain.fields = _.filter(newDomain.fields, (field) => field.name !== keyField.javaFieldName); + + newDomain.queryKeyFields = []; + } + + // Exclude key column from indexes. + _.forEach(newDomain.indexes, (index) => { + index.fields = _.filter(index.fields, (field) => field.name !== keyField.javaFieldName); + }); + + newDomain.indexes = _.filter(newDomain.indexes, (index) => !_.isEmpty(index.fields)); + } + + // Prepare caches for generation. + if (table.action === IMPORT_DM_NEW_CACHE) { + const newCache = angular.copy(this.loadedCaches[table.cacheOrTemplate]); + + batchAction.newCache = newCache; + + // const siblingCaches = batch.filter((a) => a.newCache).map((a) => a.newCache); + const siblingCaches = []; + newCache._id = ObjectID.generate(); + newCache.name = uniqueName(typeName + 'Cache', this.caches.concat(siblingCaches)); + newCache.domains = [batchAction.newDomainModel._id]; + batchAction.newDomainModel.caches = [newCache._id]; + + // POJO store factory is not defined in template. + if (!newCache.cacheStoreFactory || newCache.cacheStoreFactory.kind !== 'CacheJdbcPojoStoreFactory') { + const dialect = $scope.importDomain.demo ? 'H2' : $scope.selectedPreset.db; + + const catalog = $scope.importDomain.catalog; + + newCache.cacheStoreFactory = { + kind: 'CacheJdbcPojoStoreFactory', + CacheJdbcPojoStoreFactory: { + dataSourceBean: 'ds' + dialect + '_' + catalog, + dialect + }, + CacheJdbcBlobStoreFactory: { connectVia: 'DataSource' } + }; + } + + if (!newCache.readThrough && !newCache.writeThrough) { + newCache.readThrough = true; + newCache.writeThrough = true; + } + } + else { + const newDomain = batchAction.newDomainModel; + const cacheId = table.cacheOrTemplate; + + batchAction.newDomainModel.caches = [cacheId]; + + if (!_.includes(checkedCaches, cacheId)) { + const cache = _.find($scope.caches, {value: cacheId}).cache; + + // TODO: move elsewhere, make sure it still works + const change = LegacyUtils.autoCacheStoreConfiguration(cache, [newDomain]); + + if (change) + batchAction.cacheStoreChanges = [{cacheId, change}]; + + checkedCaches.push(cacheId); + } + } + + batch.push(batchAction); + }); + + /** + * Generate message to show on confirm dialog. + * + * @param meta Object to confirm. + * @returns {string} Generated message. + */ + function overwriteMessage(meta) { + return ` + Domain model with name "${meta.newDomainModel.databaseTable}" already exists. + Are you sure you want to overwrite it? + `; + } + + const itemsToConfirm = _.filter(batch, (item) => item.confirm); + + const checkOverwrite = () => { + if (itemsToConfirm.length > 0) { + return ConfirmBatch.confirm(overwriteMessage, itemsToConfirm) + .then(() => this.saveBatch(_.filter(batch, (item) => !item.skip))) + .catch(() => Messages.showError('Importing of domain models interrupted by user.')); + } + return this.saveBatch(batch); + }; + + const checkDuplicate = () => { + if (containDup) { + Confirm.confirm('Some tables have the same name.<br/>' + + 'Name of types for that tables will contain schema name too.') + .then(() => checkOverwrite()); + } + else + checkOverwrite(); + }; + + if (containKey) + checkDuplicate(); + else { + Confirm.confirm('Some tables have no primary key.<br/>' + + 'You will need to configure key type and key fields for such tables after import complete.') + .then(() => checkDuplicate()); + } + }; + + + $scope.importDomainNext = (form) => { + if (!$scope.importDomainNextAvailable()) + return; + + const act = $scope.importDomain.action; + + if (act === 'drivers' && $scope.importDomain.jdbcDriversNotFound) + importDomainModal.hide(); + else if (act === 'connect') + _loadSchemas(); + else if (act === 'schemas') + _loadTables(); + else if (act === 'tables') + _selectOptions(); + else if (act === 'options') + _saveDomainModel(form); + }; + + $scope.nextTooltipText = function() { + const importDomainNextAvailable = $scope.importDomainNextAvailable(); + + const act = $scope.importDomain.action; + + if (act === 'drivers' && $scope.importDomain.jdbcDriversNotFound) + return 'Resolve issue with JDBC drivers<br>Close this dialog and try again'; + + if (act === 'connect' && _.isNil($scope.selectedPreset.jdbcDriverClass)) + return 'Input valid JDBC driver class name'; + + if (act === 'connect' && _.isNil($scope.selectedPreset.jdbcUrl)) + return 'Input valid JDBC URL'; + + if (act === 'connect' || act === 'drivers') + return 'Click to load list of schemas from database'; + + if (act === 'schemas') + return importDomainNextAvailable ? 'Click to load list of tables from database' : 'Select schemas to continue'; + + if (act === 'tables') + return importDomainNextAvailable ? 'Click to show import options' : 'Select tables to continue'; + + if (act === 'options') + return 'Click to import domain model for selected tables'; + + return 'Click to continue'; + }; + + $scope.prevTooltipText = function() { + const act = $scope.importDomain.action; + + if (act === 'schemas') + return $scope.importDomain.demo ? 'Click to return on demo description step' : 'Click to return on connection configuration step'; + + if (act === 'tables') + return 'Click to return on schemas selection step'; + + if (act === 'options') + return 'Click to return on tables selection step'; + }; + + $scope.importDomainNextAvailable = function() { + switch ($scope.importDomain.action) { + case 'connect': + return !_.isNil($scope.selectedPreset.jdbcDriverClass) && !_.isNil($scope.selectedPreset.jdbcUrl); + + case 'schemas': + return _.isEmpty($scope.importDomain.schemas) || !!get('importDomain.schemasToUse.length')($scope); + + case 'tables': + return !!$scope.importDomain.tablesToUse.length; + + default: + return true; + } + }; + + $scope.importDomainPrev = function() { + $scope.importDomain.button = 'Next'; + + if ($scope.importDomain.action === 'options') { + $scope.importDomain.action = 'tables'; + $scope.importDomain.info = INFO_SELECT_TABLES; + } + else if ($scope.importDomain.action === 'tables' && $scope.importDomain.schemas.length > 0) { + $scope.importDomain.action = 'schemas'; + $scope.importDomain.info = INFO_SELECT_SCHEMAS; + } + else { + $scope.importDomain.action = 'connect'; + $scope.importDomain.info = INFO_CONNECT_TO_DB; + } + }; + + const demo = $root.IgniteDemoMode; + + $scope.importDomain = { + demo, + action: demo ? 'connect' : 'drivers', + jdbcDriversNotFound: demo, + schemas: [], + allSchemasSelected: false, + tables: [], + allTablesSelected: false, + button: 'Next', + info: '' + }; + + $scope.importDomain.loadingOptions = LOADING_JDBC_DRIVERS; + + agentMgr.startAgentWatch('Back', this.$uiRouter.globals.current.name) + .then(() => { + ActivitiesData.post({ + group: 'configuration', + action: 'configuration/import/model' + }); + + return true; + }) + .then(() => { + if (demo) { + $scope.ui.packageNameUserInput = $scope.ui.packageName; + $scope.ui.packageName = 'model'; + + return; + } + + // Get available JDBC drivers via agent. + Loading.start('importDomainFromDb'); + + $scope.jdbcDriverJars = []; + $scope.ui.selectedJdbcDriverJar = {}; + + return agentMgr.drivers() + .then((drivers) => { + $scope.ui.packageName = $scope.ui.packageNameUserInput; + + if (drivers && drivers.length > 0) { + drivers = _.sortBy(drivers, 'jdbcDriverJar'); + + _.forEach(drivers, (drv) => { + $scope.jdbcDriverJars.push({ + label: drv.jdbcDriverJar, + value: { + jdbcDriverJar: drv.jdbcDriverJar, + jdbcDriverClass: drv.jdbcDriverCls + } + }); + }); + + $scope.ui.selectedJdbcDriverJar = $scope.jdbcDriverJars[0].value; + + // FormUtils.confirmUnsavedChanges(dirty, () => { + $scope.importDomain.action = 'connect'; + $scope.importDomain.tables = []; + this.selectedTables = []; + + // Focus.move('jdbcUrl'); + // }); + } + else { + $scope.importDomain.jdbcDriversNotFound = true; + $scope.importDomain.button = 'Cancel'; + } + }) + .then(() => { + $scope.importDomain.info = INFO_CONNECT_TO_DB; + + Loading.finish('importDomainFromDb'); + }); + }); + + $scope.$watch('ui.selectedJdbcDriverJar', function(val) { + if (val && !$scope.importDomain.demo) { + const foundPreset = _findPreset(val); + + const selectedPreset = $scope.selectedPreset; + + selectedPreset.db = foundPreset.db; + selectedPreset.jdbcDriverJar = foundPreset.jdbcDriverJar; + selectedPreset.jdbcDriverClass = foundPreset.jdbcDriverClass; + selectedPreset.jdbcUrl = foundPreset.jdbcUrl; + selectedPreset.user = foundPreset.user; + } + }, true); + } + + _fillCommonCachesOrTemplates(item) { + return (action) => { + if (item.cachesOrTemplates) + item.cachesOrTemplates.length = 0; + else + item.cachesOrTemplates = []; + + if (action === IMPORT_DM_NEW_CACHE) + item.cachesOrTemplates.push(...CACHE_TEMPLATES); + + if (!_.isEmpty(this.$scope.caches)) { + item.cachesOrTemplates.push(...this.$scope.caches); + this.onCacheSelect(item.cachesOrTemplates[0].value); + } + + if ( + !_.find(item.cachesOrTemplates, {value: item.cacheOrTemplate}) && + item.cachesOrTemplates.length + ) + item.cacheOrTemplate = item.cachesOrTemplates[0].value; + }; + } + + schemasColumnDefs = [ + { + name: 'name', + displayName: 'Name', + field: 'name', + enableHiding: false, + sort: {direction: 'asc', priority: 0}, + filter: { + placeholder: 'Filter by Nameâ¦' + }, + visible: true, + sortingAlgorithm: naturalCompare, + minWidth: 165 + } + ]; + + tablesColumnDefs = [ + { + name: 'schema', + displayName: 'Schema', + field: 'schema', + enableHiding: false, + enableFiltering: false, + sort: {direction: 'asc', priority: 0}, + visible: true, + sortingAlgorithm: naturalCompare, + minWidth: 100 + }, + { + name: 'table', + displayName: 'Table', + field: 'table', + enableHiding: false, + enableFiltering: true, + filter: { + placeholder: 'Filter by Tableâ¦' + }, + visible: true, + sortingAlgorithm: naturalCompare, + minWidth: 200 + }, + { + name: 'action', + displayName: 'Action', + field: 'action', + enableHiding: false, + enableFiltering: false, + cellTemplate: ` + <tables-action-cell + table='row.entity' + on-edit-start='grid.appScope.$ctrl.$scope.startEditDbTableCache($event)' + on-cache-select='grid.appScope.$ctrl.onCacheSelect($event)' + caches='grid.appScope.$ctrl._importCachesOrTemplates' + import-actions='grid.appScope.$ctrl.$scope.importActions' + ></tables-action-cell> + `, + visible: true, + minWidth: 500 + } + ]; +} + +export const component = { + name: 'modalImportModels', + controller: ModalImportModels, + templateUrl, + bindings: { + onHide: '&', + clusterID: '<clusterId' + } +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/index.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/index.js b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/index.js new file mode 100644 index 0000000..b75c89a --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/index.js @@ -0,0 +1,31 @@ +/* + * 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 angular from 'angular'; +import {component} from './component'; +import service from './service'; +import {component as stepIndicator} from './step-indicator/component'; +import {component as tablesActionCell} from './tables-action-cell/component'; +import {component as amountIndicator} from './selected-items-amount-indicator/component'; + +export default angular +.module('configuration.modal-import-models', []) +.service(service.name, service) +.component(tablesActionCell.name, tablesActionCell) +.component(stepIndicator.name, stepIndicator) +.component(amountIndicator.name, amountIndicator) +.component(component.name, component); http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/component.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/component.js b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/component.js new file mode 100644 index 0000000..26a6ea0 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/component.js @@ -0,0 +1,27 @@ +/* + * 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 template from './template.pug'; +import './style.scss'; +export const component = { + name: 'selectedItemsAmountIndicator', + template, + bindings: { + selectedAmount: '<', + totalAmount: '<' + } +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/style.scss ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/style.scss b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/style.scss new file mode 100644 index 0000000..c5c2a05 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/style.scss @@ -0,0 +1,24 @@ +/* + * 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. + */ + +selected-items-amount-indicator { + font-family: Roboto; + font-size: 14px; + font-style: italic; + color: #757575; + display: inline-block; +} \ 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/components/modal-import-models/selected-items-amount-indicator/template.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/template.pug b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/template.pug new file mode 100644 index 0000000..9a46a77 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/selected-items-amount-indicator/template.pug @@ -0,0 +1,17 @@ +//- + 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. + +| {{ $ctrl.selectedAmount || 0}} of {{ $ctrl.totalAmount }} selected \ 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/components/modal-import-models/service.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/service.js b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/service.js new file mode 100644 index 0000000..3c0ecbb --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/service.js @@ -0,0 +1,56 @@ +/* + * 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. + */ + +export default class ModalImportModels { + static $inject = ['$modal', 'AgentManager', '$uiRouter']; + constructor($modal, AgentManager, $uiRouter) { + Object.assign(this, {$modal, AgentManager, $uiRouter}); + } + _goToDynamicState() { + if (this._state) this.$uiRouter.stateRegistry.deregister(this._state); + this._state = this.$uiRouter.stateRegistry.register({ + name: 'importModels', + parent: this.$uiRouter.stateService.current, + onEnter: () => { + this._open(); + }, + onExit: () => { + this._modal && this._modal.hide(); + } + }); + return this.$uiRouter.stateService.go(this._state, this.$uiRouter.stateService.params); + } + _open() { + this._modal = this.$modal({ + template: ` + <modal-import-models + on-hide='$ctrl.$state.go("^")' + cluster-id='$ctrl.$state.params.clusterID' + ></modal-import-models> + `, + controller: ['$state', function($state) {this.$state = $state;}], + controllerAs: '$ctrl', + show: false + }); + return this.AgentManager.startAgentWatch('Back', this.$uiRouter.globals.current.name) + .then(() => this._modal.$promise) + .then(() => this._modal.show()); + } + open() { + this._goToDynamicState(); + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/component.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/component.js b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/component.js new file mode 100644 index 0000000..4f2b141 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/component.js @@ -0,0 +1,35 @@ +/* + * 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 template from './template.pug'; +import './style.scss'; + +export class ModalImportModelsStepIndicator { + isVisited(index) { + return index <= this.steps.findIndex((step) => step.value === this.currentStep); + } +} + +export const component = { + name: 'modalImportModelsStepIndicator', + template, + controller: ModalImportModelsStepIndicator, + bindings: { + steps: '<', + currentStep: '<' + } +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/7ee1683e/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/style.scss ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/style.scss b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/style.scss new file mode 100644 index 0000000..e841272 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/style.scss @@ -0,0 +1,101 @@ +/* + * 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. + */ + +modal-import-models-step-indicator { + @import "public/stylesheets/variables.scss"; + + $text-color-default: #393939; + $text-color-active: $ignite-brand-success; + $indicator-color-default: #757575; + $indicator-color-active: $ignite-brand-success; + $indicator-size: 12px; + $indicator-border-radius: 2px; + $spline-height: 1px; + + display: block; + font-family: Roboto; + + .step-indicator__steps { + display: flex; + flex-direction: row; + justify-content: space-between; + margin: 0; + padding: 0; + list-style: none; + } + + .step-indicator__step { + color: $text-color-default; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + + &:before { + content: ''; + display: block; + background: $indicator-color-default; + width: 100%; + height: $spline-height; + bottom: $indicator-size / 2; + position: absolute; + } + + &:after { + content: ''; + display: block; + background: $indicator-color-default; + width: $indicator-size; + height: $indicator-size; + border-radius: $indicator-border-radius; + margin-top: 5px; + z-index: 1; + } + } + .step-indicator__step-first, + .step-indicator__step-last { + &:before { + width: calc(50% - #{$indicator-size} / 2); + } + } + .step-indicator__step-first:before { + right: 0; + } + .step-indicator__step-last:before { + left: 0; + } + .step-indicator__step-active { + color: $text-color-active; + + &:after { + background: $indicator-color-active; + } + } + .step-indicator__spline { + background: $indicator-color-default; + height: $spline-height; + width: 100%; + margin-top: auto; + margin-bottom: $indicator-size / 2; + } + .step-indicator__step-visited { + &:before, + &+.step-indicator__spline { + background: $indicator-color-active; + } + } +} \ 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/components/modal-import-models/step-indicator/template.pug ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/template.pug b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/template.pug new file mode 100644 index 0000000..c196604 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/step-indicator/template.pug @@ -0,0 +1,31 @@ +//- + 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. + +nav + .step-indicator__steps + .step-indicator__step( + ng-repeat-start='step in ::$ctrl.steps' + ng-class=`{ + "step-indicator__step-active": $ctrl.currentStep === step.value, + "step-indicator__step-visited": $ctrl.isVisited($index), + "step-indicator__step-first": $first, + "step-indicator__step-last": $last + }` + ) {{::step.label}} + .step-indicator__spline( + ng-repeat-end + ng-if='!$last' + ) \ 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/components/modal-import-models/style.scss ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/style.scss b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/style.scss new file mode 100644 index 0000000..5c109b4 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/style.scss @@ -0,0 +1,53 @@ +/* + * 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. + */ + +modal-import-models { + .modal-content { + min-height: 493px; + display: flex; + flex-direction: column; + + .modal-body { + flex: 1 1 auto; + overflow-y: auto; + } + .modal-footer { + display: flex; + align-items: baseline; + + selected-items-amount-indicator { + margin-left: auto; + margin-right: auto; + } + } + } + + pc-items-table { + width: 100%; + } + + modal-import-models-step-indicator { + margin-top: 15px; + margin-bottom: 30px; + } + .#{&}__prev-button { + margin-right: auto !important; + } + .#{&}__next-button { + margin-left: auto !important; + } +} \ 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/components/modal-import-models/tables-action-cell/component.js ---------------------------------------------------------------------- diff --git a/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/tables-action-cell/component.js b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/tables-action-cell/component.js new file mode 100644 index 0000000..072a497 --- /dev/null +++ b/modules/web-console/frontend/app/components/page-configure/components/modal-import-models/tables-action-cell/component.js @@ -0,0 +1,62 @@ +/* + * 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 template from './template.pug'; +import './style.scss'; + +const IMPORT_DM_NEW_CACHE = 1; + +export class TablesActionCell { + static $inject = ['$element']; + constructor($element) { + Object.assign(this, {$element}); + } + onClick(e) { + e.stopPropagation(); + } + $postLink() { + this.$element.on('click', this.onClick); + } + $onDestroy() { + this.$element.off('click', this.onClick); + this.$element = null; + } + tableActionView(table) { + if (!this.caches) return; + const cache = this.caches.find((c) => c.value === table.cacheOrTemplate); + if (!cache) return; + const cacheName = cache.label; + + if (table.action === IMPORT_DM_NEW_CACHE) + return 'Create ' + table.generatedCacheName + ' (' + cacheName + ')'; + + return 'Associate with ' + cacheName; + } +} + +export const component = { + name: 'tablesActionCell', + controller: TablesActionCell, + bindings: { + onEditStart: '&', + onCacheSelect: '&?', + table: '<', + caches: '<', + importActions: '<' + }, + template +};
