blob: dae0eb3f34e38cceeb6543e59da7e2d8b5ae03f6 [file] [log] [blame]
/*
* 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 {combineLatest, EMPTY, from, merge, of, race, timer} from 'rxjs';
import {distinctUntilChanged, filter, map, pluck, switchMap, take, tap} from 'rxjs/operators';
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 '../../store/selectors';
import {default as ConfigEffects} from '../../store/effects';
import {default as ConfigureState} from '../../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', 'ConfigEffects', 'ConfigureState', 'IgniteConfirm', 'IgniteConfirmBatch', 'IgniteFocus', 'SqlTypes', 'JavaTypes', 'IgniteMessages', '$scope', '$rootScope', 'AgentManager', 'IgniteActivitiesData', 'IgniteLoading', 'IgniteFormUtils', 'IgniteLegacyUtils'];
/**
* @param {UIRouter} $uiRouter
* @param {ConfigSelectors} ConfigSelectors
* @param {ConfigEffects} ConfigEffects
* @param {ConfigureState} ConfigureState
* @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, Confirm, ConfirmBatch, Focus, SqlTypes, JavaTypes, Messages, $scope, $root, agentMgr, ActivitiesData, Loading, FormUtils, LegacyUtils) {
this.$uiRouter = $uiRouter;
this.ConfirmBatch = ConfirmBatch;
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 of(this.clusterID).pipe(
switchMap((id = 'new') => {
return this.ConfigureState.state$.pipe(this.ConfigSelectors.selectClusterToEdit(id, defaultNames.importedCluster));
}),
switchMap((cluster) => {
return (!(cluster.caches || []).length && !(cluster.models || []).length)
? of({
cluster,
caches: [],
models: []
})
: from(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})
])).pipe(switchMap(() => {
return combineLatest(
this.ConfigureState.state$.pipe(this.ConfigSelectors.selectShortCachesValue()),
this.ConfigureState.state$.pipe(this.ConfigSelectors.selectShortModelsValue()),
(caches, models) => ({
cluster,
caches,
models
})
).pipe(take(1));
}));
}),
take(1)
);
}
saveBatch(batch) {
if (!batch.length)
return;
this.$scope.importDomain.loadingOptions = SAVING_DOMAINS;
this.Loading.start('importDomainFromDb');
this.ConfigureState.dispatchAction({
type: 'ADVANCED_SAVE_COMPLETE_CONFIGURATION',
changedItems: this.batchActionsToRequestBody(batch),
prevActions: []
});
this.saveSubscription = race(
this.ConfigureState.actions$.pipe(
filter((a) => a.type === 'ADVANCED_SAVE_COMPLETE_CONFIGURATION_OK'),
tap(() => this.onHide())
),
this.ConfigureState.actions$.pipe(
filter((a) => a.type === 'ADVANCED_SAVE_COMPLETE_CONFIGURATION_ERR')
)
).pipe(
take(1),
tap(() => {
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 = merge(
timer(0, 1).pipe(
take(1),
tap(() => this.ConfigureState.dispatchAction({type: 'LOAD_CACHE', cacheID}))
),
race(
this.ConfigureState.actions$.pipe(
filter((a) => a.type === 'LOAD_CACHE_OK' && a.cache._id === cacheID),
pluck('cache'),
tap((cache) => {
this.loadedCaches[cacheID] = cache;
})
),
this.ConfigureState.actions$.pipe(
filter((a) => a.type === 'LOAD_CACHE_ERR' && a.action.cacheID === cacheID)
)
).pipe(take(1))
)
.subscribe();
}
$onDestroy() {
this.subscribers$.unsubscribe();
if (this.onCacheSelectSubcription) this.onCacheSelectSubcription.unsubscribe();
if (this.saveSubscription) this.saveSubscription.unsubscribe();
}
$onInit() {
// Restores old behavior
const {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().pipe(tap((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;
}));
// 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: 'com.mysql.cj.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;
result.jdbcDriverImplementationVersion = selectedJdbcJar.jdbcDriverImplementationVersion;
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 = _.cloneDeep(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,
implementationVersion: $scope.selectedPreset.jdbcDriverImplementationVersion
},
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 &quot;${meta.newDomainModel.databaseTable}&quot; 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;
const fetchDomainData = () => {
return agentMgr.awaitAgent()
.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,
jdbcDriverImplementationVersion: drv.jdbcDriverImplVersion
}
});
});
$scope.ui.selectedJdbcDriverJar = $scope.jdbcDriverJars[0].value;
$scope.importDomain.action = 'connect';
$scope.importDomain.tables = [];
this.selectedTables = [];
}
else {
$scope.importDomain.jdbcDriversNotFound = true;
$scope.importDomain.button = 'Cancel';
}
})
.then(() => {
$scope.importDomain.info = INFO_CONNECT_TO_DB;
Loading.finish('importDomainFromDb');
});
});
};
this.agentIsAvailable$ = this.agentMgr.connectionSbj.pipe(
pluck('state'),
distinctUntilChanged(),
map((state) => state !== 'AGENT_DISCONNECTED')
);
this.domainData$ = this.agentIsAvailable$.pipe(
switchMap((agentIsAvailable) => {
if (!agentIsAvailable)
return of(EMPTY);
return from(fetchDomainData());
})
);
this.subscribers$ = merge(
this.subscription,
this.domainData$
).subscribe();
$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.jdbcDriverImplementationVersion = foundPreset.jdbcDriverImplementationVersion;
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: 450
}
];
}
export const component = {
name: 'modalImportModels',
controller: ModalImportModels,
templateUrl,
bindings: {
onHide: '&',
clusterID: '<clusterId'
}
};