UNPKG

strong-arc

Version:

A visual suite for the StrongLoop API Platform

567 lines (495 loc) 18 kB
// Copyright StrongLoop 2014 Model.service('ModelService', [ 'Modeldef', 'ModelDefinition', 'ModelConfig', '$q', 'AppStorageService', 'ModelProperty', 'DataSourceDefinition', 'connectorMetadata', function(Modeldef, ModelDefinition, ModelConfig, $q, AppStorageService, ModelProperty, DataSourceDefinition, connectorMetadata) { var svc = {}; svc.createModelInstance = function(targetInstance) { if (targetInstance.definition.name) { delete targetInstance.id; delete targetInstance.definition.id; return ModelDefinition.create({}, targetInstance.definition) .$promise .then(function(definition) { targetInstance.id = definition.id; targetInstance.name = definition.name; targetInstance.definition = definition; targetInstance.config = angular.extend({ name: definition.name, facetName: CONST.APP_FACET }, targetInstance.config); return ModelConfig.create(targetInstance.config) .$promise .then(function(config) { targetInstance.config = config; return svc.isModelConfigMigrateable(targetInstance .config) .then(function(canMigrate) { targetInstance.canMigrate = canMigrate; return targetInstance; }). catch(function(error) { log.warn('bad is model config migratable: ' + error); return error; }); }) .catch(function(error) { console.warn('bad create model config: ' + error); return error; }); }) .catch(function(error){ console.warn('bad create model def: ' + error); return error; }); } else { console.warn('createModelInstance called with no definition name'); return null; } }; // returns a full 'instance' with a definition and config property svc.getModelInstanceById = function(modelId) { var deferred = $q.defer(); var instance = {}; svc.getModelDefinitionById(modelId). // model definition then(function(definition) { // begin the instance object // add the definition instance = { definition: definition, type: CONST.MODEL_TYPE, id: definition.id, name: definition.name }; return instance; }). // model config then(function(instance) { // get the config svc.getModelConfigByName(instance.name). then(function(response) { instance.config = response; if (instance.config.dataSource) { if (instance.config.dataSource === null) { instance.config.dataSource = CONST.DEFAULT_DATASOURCE; } } else { instance.config.dataSource = CONST.DEFAULT_DATASOURCE; } return instance; }). then(function(instance) { // model properties svc.getModelPropertiesById(instance.id). then(function(properties) { instance.properties = properties; svc.isModelConfigMigrateable(instance.config) .then(function(canMigrate) { instance.canMigrate = canMigrate; deferred.resolve(instance); }); }); }). catch(function(error) { console.warn('bad get model config'); return error; }); }). catch(function(error) { deferred.reject(error); }); return deferred.promise; }; svc.deleteModelInstance = function(definitionId, configId) { if (definitionId) { var deferred = $q.defer(); ModelDefinition.deleteById({ id: definitionId }, function(response) { ModelConfig.deleteById({ id: configId }, function() { deferred.resolve(response); }, function(response) { console.warn('Cannot delete ModelConfig.', response); }); }, function(response) { console.warn('bad delete model definition', response); } ); return deferred.promise; } }; svc.getAllModelInstances = function() { var deferred = $q.defer(); ModelConfig.find({}, function(configs) { var configMap = {}; angular.forEach(configs, function(value, key) { configMap[value.name] = value; }); ModelDefinition.find({ filter: { where: { readonly: false } } }, function(response) { // add create model to this for new model var core = response; var log = []; var modelInstances = []; angular.forEach(core, function(value, key) { var instance = {}; instance.id = value.id; instance.name = value.name; instance.type = CONST.MODEL_TYPE; instance.definition = value; var lOptions = []; if (value.options) { angular.forEach(value.options, function(value, key) { lOptions.push({name: key, props: value}); }); instance.options = lOptions; } instance.config = configMap[value.name]; if (instance.config.dataSource) { if (instance.config.dataSource === null) { instance.config.dataSource = CONST.DEFAULT_DATASOURCE; } } else { instance.config.dataSource = CONST.DEFAULT_DATASOURCE; } modelInstances.push(instance); }, log); deferred.resolve(modelInstances); }, function(response) { console.warn('bad get model defs: ' + response); } ); }, function(err) { console.warn('Cannot get model configs.', err); } ); return deferred.promise; }; svc.updateModelInstance = function(targetInstance) { var deferred = $q.defer(); if (targetInstance.definition.id) { // `id` is '{facet}.{name}' var oldName = targetInstance.definition.id.split('.')[1]; // Temporary workaround until loopback-workspace supports renames if (oldName === targetInstance.definition.name) { ModelDefinition.upsert(targetInstance.definition, function(definition) { targetInstance.definition = definition; targetInstance.config = angular.extend({ name: definition.name, facetName: CONST.APP_FACET }, targetInstance.config); if (targetInstance.config.dataSource && (targetInstance.config.dataSource === CONST.NULL_DATASOURCE)){ targetInstance.config.dataSource = null; } ModelConfig.upsert(targetInstance.config, function(configResponse) { targetInstance.config = configResponse; svc.isModelConfigMigrateable(targetInstance.config) .then(function(canMigrate) { targetInstance.canMigrate = canMigrate; deferred.resolve(targetInstance); }); }, function(error) { console.warn('Cannot update model configuration', targetInstance.id, error); } ); }, function(error) { console.warn('bad update model definition: [' + targetInstance.id + '][' + error + ']'); } ); } else { var oldId = targetInstance.definition.id; // delete properties that should be generated from the new name delete targetInstance.definition.id; delete targetInstance.definition.configFile; targetInstance.definition = ModelDefinition.create(targetInstance.definition); targetInstance.definition.$promise .then(function moveAllRelatedDataToNewModel() { return $q.all( ['properties', 'validations', 'relations', 'accessControls'] .map(function moveToNewModel(relationName) { var entities = ModelDefinition[relationName]({ id: oldId }); return entities.$promise .then(function updateModelId() { return $q.all(entities.map(function(it) { it.modelId = targetInstance.definition.id; return it.$save(); })); }) .then(function addToLocalModel() { // is populated with model properties targetInstance[relationName] = entities; }); })); }) .then(function renameModelConfig() { var modelConfig = targetInstance.config; var oldConfigId = modelConfig.id; modelConfig.name = targetInstance.definition.name; // delete properties that should be generated from the new name delete modelConfig.id; delete modelConfig.configFile; var updatedConfig = ModelConfig.create(modelConfig); return updatedConfig.$promise .then(function updateConfigReference() { targetInstance.config = updatedConfig; }) .then(function deleteOldModelConfig() { if (!oldConfigId) return; return ModelConfig.deleteById({ id: oldConfigId }).$promise; }); }) .then(function deleteOldModel() { return ModelDefinition.deleteById({ id: oldId }).$promise; }) .then(function() { // ensure they are in sync targetInstance.id = targetInstance.definition.id; targetInstance.name = targetInstance.definition.name; deferred.resolve(targetInstance); }) .catch(function(err) { console.warn('Cannot rename %s to %s.', oldId, model.name, err); }); } } return deferred.promise; }; var DefaultModelInstance = function(){ var schema = { id: CONST.NEW_MODEL_PRE_ID, type: CONST.MODEL_TYPE, name: CONST.NEW_MODEL_NAME, definition: { id: CONST.NEW_MODEL_PRE_ID, facetName: CONST.NEW_MODEL_FACET_NAME, strict: false, name: CONST.NEW_MODEL_NAME, idInjection: false, base: CONST.NEW_MODEL_BASE }, properties: [], config: { facetName: CONST.APP_FACET, public: true, dataSource: CONST.DEFAULT_DATASOURCE } }; return schema; }; svc.createNewModelInstance = function() { var returnInstance = new DefaultModelInstance(); return returnInstance; }; svc.getModelDefinitionById = function(modelId) { var deferred = $q.defer(); if (modelId !== CONST.NEW_MODEL_PRE_ID) { ModelDefinition.findById({id:modelId}, function(definition) { deferred.resolve(definition); }, function(error) { deferred.reject(error); }); } else { console.warn('attempt to retrieve new model definition'); deferred.reject('attempt to retrieve new model definition'); } return deferred.promise; }; svc.getModelPropertiesById = function(modelId) { var deferred = $q.defer(); ModelDefinition.properties({id:modelId}, function(properties) { deferred.resolve(properties); }, function(error) { console.warn('couldnt get properties for model'); deferred.reject(error); }); return deferred.promise; }; svc.getModelConfigByName = function(modelName){ var deferred = $q.defer(); ModelConfig.findOne( { filter: { where: { facetName: CONST.APP_FACET, name: modelName } } }, function(config) { deferred.resolve(config); }, function(error) { deferred.reject(error); }); return deferred.promise; }; svc.generateModelsFromSchema = function(schemaCollection) { if (schemaCollection && schemaCollection.length > 0) { var openInstances = AppStorageService.getItem('openInstanceRefs'); if (!openInstances) { openInstances = []; } for (var i = 0;i < schemaCollection.length;i++) { var sourceDbModelObj = schemaCollection[i]; // model definition object generation from schema // TODO map db source name here var newLBModel = { id: sourceDbModelObj.id, name: sourceDbModelObj.name, type:'model' }; // open instances reset openInstances.push({ id: sourceDbModelObj.id, name: sourceDbModelObj.name, type:'model' }); var modelProps = { config: { public: true }, plural: sourceDbModelObj.name + 's' }; newLBModel.props = modelProps; if (sourceDbModelObj.properties) { for (var k = 0;k < sourceDbModelObj.properties.length;k++){ var sourceProperty = sourceDbModelObj.properties[k]; var targetDataType = 'string'; // lock the varchar type if (sourceProperty.dataType.toLowerCase().indexOf('varchar') > -1) { sourceProperty.dataType = 'varchar'; } // TODO hopefully this conversion will happen in the API switch (sourceProperty.dataType) { case 'int': targetDataType = 'Number'; break; case 'varchar': targetDataType = 'String'; break; case 'datetime': targetDataType = 'Date'; break; case 'timestamp': targetDataType = 'Date'; break; case 'char': targetDataType = 'String'; break; case 'tinytext': targetDataType = 'String'; break; case 'longtext': targetDataType = 'String'; break; case 'point': targetDataType = 'GeoPoint'; break; default: targetDataType = 'String'; } var propertyProps = { type:targetDataType }; newLBModel.props.properties.push({ name:sourceProperty.columnName, props:propertyProps }); } svc.createModelDefinition(newLBModel); } } // open the new models AppStorageService.setItem('openInstanceRefs', openInstances); // activate the last one AppStorageService.setItem('activeInstance', newLBModel); // activate the first of the index // IAService.activeInstance = svc.activate //var nm = IAService.activateInstanceByName(newOpenInstances[0], 'model'); } }; svc.migrateModelConfig = function(config) { var deferred = $q.defer(); var promise = deferred.promise; var filter = { filter: { where: { facetName: CONST.APP_FACET } } }; if (config.dataSource === CONST.DEFAULT_DATASOURCE) { config.dataSource = null; } if(config.dataSource) { filter.filter.where.name = config.dataSource; } return DataSourceDefinition.findOne(filter) .$promise .then(function(dataSourceDef) { return DataSourceDefinition.prototype$autoupdate({ id: dataSourceDef.id },{ modelName: config.name }).$promise; }); } svc.isModelConfigMigrateable = function(config) { var deferred = $q.defer(); var promise = deferred.promise; var canMigrate = false; if (config.dataSource === CONST.NULL_DATASOURCE) { config.dataSource = null; } if(!config || !config.dataSource) { deferred.resolve(canMigrate); return promise; } DataSourceDefinition.findOne({ filter: { where: { name: config.dataSource, facetName: CONST.APP_FACET } } }, function(dataSourceDef) { var connectorName = dataSourceDef && dataSourceDef.connector; var metadata = connectorMetadata.filter(function(it) { return it.name === connectorName; })[0]; var connectorIsSupported = connectorName && metadata && metadata.features && metadata.features.migration; deferred.resolve(dataSourceDef && connectorIsSupported); }); return promise; } return svc; } ]);