Merge pull request #420 from aol-nnov/fkCustomDbType
Create model foreign key matching type of opposite part of relation (even if it has a custom field type)
This commit is contained in:
commit
e9c966227d
|
@ -0,0 +1,13 @@
|
|||
# editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -188,8 +188,8 @@ function connectorModuleNames(name) {
|
|||
}
|
||||
}
|
||||
// Only try the short name if the connector is not from StrongLoop
|
||||
if(['mongodb', 'oracle', 'mysql', 'postgresql', 'mssql', 'rest', 'soap']
|
||||
.indexOf(name) === -1) {
|
||||
if (['mongodb', 'oracle', 'mysql', 'postgresql', 'mssql', 'rest', 'soap']
|
||||
.indexOf(name) === -1) {
|
||||
names.push(name);
|
||||
}
|
||||
return names;
|
||||
|
@ -225,7 +225,7 @@ DataSource._resolveConnector = function (name, loader) {
|
|||
if (!connector) {
|
||||
error = util.format('\nWARNING: LoopBack connector "%s" is not installed ' +
|
||||
'as any of the following modules:\n\n %s\n\nTo fix, run:\n\n npm install %s\n',
|
||||
name, names.join('\n'), names[names.length -1]);
|
||||
name, names.join('\n'), names[names.length - 1]);
|
||||
}
|
||||
return {
|
||||
connector: connector,
|
||||
|
@ -265,7 +265,7 @@ DataSource.prototype.setup = function (name, settings) {
|
|||
|
||||
this.settings.debug = this.settings.debug || debug.enabled;
|
||||
|
||||
if(this.settings.debug) {
|
||||
if (this.settings.debug) {
|
||||
debug('Settings: %j', this.settings);
|
||||
}
|
||||
|
||||
|
@ -379,8 +379,8 @@ function isModelDataSourceAttached(model) {
|
|||
* @param scopes
|
||||
*/
|
||||
DataSource.prototype.defineScopes = function (modelClass, scopes) {
|
||||
if(scopes) {
|
||||
for(var s in scopes) {
|
||||
if (scopes) {
|
||||
for (var s in scopes) {
|
||||
defineScope(modelClass, modelClass, s, scopes[s], {}, scopes[s].options);
|
||||
}
|
||||
}
|
||||
|
@ -429,15 +429,15 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
|||
|
||||
// Set up the relations
|
||||
if (relations) {
|
||||
Object.keys(relations).forEach(function(rn) {
|
||||
Object.keys(relations).forEach(function (rn) {
|
||||
var r = relations[rn];
|
||||
assert(DataSource.relationTypes.indexOf(r.type) !== -1, "Invalid relation type: " + r.type);
|
||||
var targetModel, polymorphicName;
|
||||
|
||||
|
||||
if (r.polymorphic && r.type !== 'belongsTo' && !r.model) {
|
||||
throw new Error('No model specified for polymorphic ' + r.type + ': ' + rn);
|
||||
}
|
||||
|
||||
|
||||
if (r.polymorphic) {
|
||||
polymorphicName = typeof r.model === 'string' ? r.model : rn;
|
||||
if (typeof r.polymorphic === 'string') {
|
||||
|
@ -446,17 +446,17 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
|||
polymorphicName = r.polymorphic.as;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (r.model) {
|
||||
targetModel = isModelClass(r.model) ? r.model : self.getModel(r.model, true);
|
||||
}
|
||||
|
||||
|
||||
var throughModel = null;
|
||||
if (r.through) {
|
||||
throughModel = isModelClass(r.through) ? r.through : self.getModel(r.through, true);
|
||||
}
|
||||
|
||||
if ((targetModel && !isModelDataSourceAttached(targetModel))
|
||||
|
||||
if ((targetModel && !isModelDataSourceAttached(targetModel))
|
||||
|| (throughModel && !isModelDataSourceAttached(throughModel))) {
|
||||
// Create a listener to defer the relation set up
|
||||
createListener(rn, r, targetModel, throughModel);
|
||||
|
@ -484,13 +484,13 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) {
|
|||
// Check if the id property should be generated
|
||||
var idName = modelClass.definition.idName();
|
||||
var idProp = modelClass.definition.rawProperties[idName];
|
||||
if(idProp && idProp.generated && this.connector.getDefaultIdType) {
|
||||
if (idProp && idProp.generated && this.connector.getDefaultIdType) {
|
||||
// Set the default id type from connector's ability
|
||||
var idType = this.connector.getDefaultIdType() || String;
|
||||
idProp.type = idType;
|
||||
modelClass.definition.properties[idName].type = idType;
|
||||
if (settings.forceId) {
|
||||
modelClass.validatesAbsenceOf(idName, { if: 'isNewRecord' });
|
||||
modelClass.validatesAbsenceOf(idName, {if: 'isNewRecord'});
|
||||
}
|
||||
}
|
||||
if (this.connector.define) {
|
||||
|
@ -525,7 +525,7 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) {
|
|||
* The first (String) argument specifying the model name is required.
|
||||
* You can provide one or two JSON object arguments, to provide configuration options.
|
||||
* See [Model definition reference](http://docs.strongloop.com/display/DOC/Model+definition+reference) for details.
|
||||
*
|
||||
*
|
||||
* Simple example:
|
||||
* ```
|
||||
* var User = dataSource.createModel('User', {
|
||||
|
@ -546,7 +546,7 @@ DataSource.prototype.setupDataAccess = function (modelClass, settings) {
|
|||
* });
|
||||
* ```
|
||||
* You can also define an ACL when you create a new data source with the `DataSource.create()` method. For example:
|
||||
*
|
||||
*
|
||||
* ```js
|
||||
* var Customer = ds.createModel('Customer', {
|
||||
* name: {
|
||||
|
@ -749,9 +749,9 @@ DataSource.prototype.defineProperty = function (model, prop, params) {
|
|||
/**
|
||||
* Drop each model table and re-create.
|
||||
* This method applies only to database connectors. For MongoDB, it drops and creates indexes.
|
||||
*
|
||||
*
|
||||
* **WARNING**: Calling this function deletes all data! Use `autoupdate()` to preserve data.
|
||||
*
|
||||
*
|
||||
* @param {String} model Model to migrate. If not present, apply to all models. Can also be an array of Strings.
|
||||
* @param {Function} [callback] Callback function. Optional.
|
||||
*
|
||||
|
@ -784,15 +784,15 @@ DataSource.prototype.automigrate = function (models, cb) {
|
|||
return cb && process.nextTick(cb);
|
||||
}
|
||||
|
||||
var invalidModels = models.filter(function(m) {
|
||||
var invalidModels = models.filter(function (m) {
|
||||
return !(m in attachedModels);
|
||||
});
|
||||
|
||||
if (invalidModels.length) {
|
||||
return process.nextTick(function() {
|
||||
return process.nextTick(function () {
|
||||
if (cb) {
|
||||
cb(new Error('Cannot migrate models not attached to this datasource: ' +
|
||||
invalidModels.join(' ')));
|
||||
invalidModels.join(' ')));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -808,7 +808,7 @@ DataSource.prototype.automigrate = function (models, cb) {
|
|||
* @param {String} model Model to migrate. If not present, apply to all models. Can also be an array of Strings.
|
||||
* @param {Function} [cb] The callback function
|
||||
*/
|
||||
DataSource.prototype.autoupdate = function(models, cb) {
|
||||
DataSource.prototype.autoupdate = function (models, cb) {
|
||||
this.freeze();
|
||||
|
||||
if ((!cb) && ('function' === typeof models)) {
|
||||
|
@ -835,15 +835,15 @@ DataSource.prototype.autoupdate = function(models, cb) {
|
|||
return process.nextTick(cb);
|
||||
}
|
||||
|
||||
var invalidModels = models.filter(function(m) {
|
||||
var invalidModels = models.filter(function (m) {
|
||||
return !(m in attachedModels);
|
||||
});
|
||||
|
||||
if (invalidModels.length) {
|
||||
return process.nextTick(function() {
|
||||
return process.nextTick(function () {
|
||||
if (cb) {
|
||||
cb(new Error('Cannot migrate models not attached to this datasource: ' +
|
||||
invalidModels.join(' ')));
|
||||
invalidModels.join(' ')));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1027,7 +1027,7 @@ DataSource.prototype.discoverForeignKeysSync = function (modelName, options) {
|
|||
/**
|
||||
* Retrieves a description of the foreign key columns that reference the given table's primary key columns
|
||||
* (the foreign keys exported by a table), ordered by fkTableOwner, fkTableName, and keySeq.
|
||||
*
|
||||
*
|
||||
* Callback function return value is an object that can have the following properties:
|
||||
*
|
||||
*| Key | Type | Description |
|
||||
|
@ -1092,7 +1092,7 @@ function fromDBName(dbName, camelCase) {
|
|||
|
||||
/**
|
||||
* Discover one schema from the given model without following the relations.
|
||||
**Example schema from oracle connector:**
|
||||
**Example schema from oracle connector:**
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
|
@ -1194,7 +1194,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
|
|||
|
||||
var tasks = [
|
||||
this.discoverModelProperties.bind(this, modelName, options),
|
||||
this.discoverPrimaryKeys.bind(this, modelName, options) ];
|
||||
this.discoverPrimaryKeys.bind(this, modelName, options)];
|
||||
|
||||
var followingRelations = options.associations || options.relations;
|
||||
if (followingRelations) {
|
||||
|
@ -1230,8 +1230,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
|
|||
options: {
|
||||
idInjection: false // DO NOT add id property
|
||||
},
|
||||
properties: {
|
||||
}
|
||||
properties: {}
|
||||
};
|
||||
|
||||
schema.options[schemaName] = {
|
||||
|
@ -1246,7 +1245,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
|
|||
schema.properties[propName] = {
|
||||
type: item.type,
|
||||
required: (item.nullable === 'N' || item.nullable === 'NO'
|
||||
|| item.nullable === 0 || item.nullable === false),
|
||||
|| item.nullable === 0 || item.nullable === false),
|
||||
length: item.dataLength,
|
||||
precision: item.dataPrecision,
|
||||
scale: item.dataScale
|
||||
|
@ -1373,8 +1372,7 @@ DataSource.prototype.discoverSchemasSync = function (modelName, options) {
|
|||
options: {
|
||||
idInjection: false // DO NOT add id property
|
||||
},
|
||||
properties: {
|
||||
}
|
||||
properties: {}
|
||||
};
|
||||
|
||||
schema.options[schemaName] = {
|
||||
|
@ -1600,7 +1598,7 @@ DataSource.prototype.log = function (sql, t) {
|
|||
* Freeze dataSource. Behavior depends on connector
|
||||
*/
|
||||
DataSource.prototype.freeze = function freeze() {
|
||||
if(!this.connector) {
|
||||
if (!this.connector) {
|
||||
throw new Error('The connector has not been initialized.');
|
||||
}
|
||||
if (this.connector.freezeDataSource) {
|
||||
|
@ -1711,11 +1709,18 @@ DataSource.prototype.defineForeignKey = function defineForeignKey(className, key
|
|||
return;
|
||||
}
|
||||
|
||||
var fkDef = {type: pkType};
|
||||
var foreignMeta = this.columnMetadata(foreignClassName, key);
|
||||
if(foreignMeta && foreignMeta.dataType) {
|
||||
fkDef[this.connector.name] = {};
|
||||
fkDef[this.connector.name].dataType = foreignMeta.dataType;
|
||||
}
|
||||
if (this.connector.defineForeignKey) {
|
||||
var cb = function (err, keyType) {
|
||||
if (err) throw err;
|
||||
fkDef.type = keyType || pkType;
|
||||
// Add the foreign key property to the data source _models
|
||||
this.defineProperty(className, key, {type: keyType || pkType});
|
||||
this.defineProperty(className, key, fkDef);
|
||||
}.bind(this);
|
||||
switch (this.connector.defineForeignKey.length) {
|
||||
case 4:
|
||||
|
@ -1728,7 +1733,7 @@ DataSource.prototype.defineForeignKey = function defineForeignKey(className, key
|
|||
}
|
||||
} else {
|
||||
// Add the foreign key property to the data source _models
|
||||
this.defineProperty(className, key, {type: pkType});
|
||||
this.defineProperty(className, key, fkDef);
|
||||
}
|
||||
|
||||
};
|
||||
|
@ -1849,7 +1854,7 @@ DataSource.prototype.enableRemote = function (operation) {
|
|||
/**
|
||||
* Disable remote access to a data source operation. Each [connector](#connector) has its own set of set enabled
|
||||
* and disabled operations. To list the operations, call `dataSource.operations()`.
|
||||
*
|
||||
*
|
||||
*```js
|
||||
* var oracle = loopback.createDataSource({
|
||||
* connector: require('loopback-connector-oracle'),
|
||||
|
@ -1859,7 +1864,7 @@ DataSource.prototype.enableRemote = function (operation) {
|
|||
* oracle.disableRemote('destroyAll');
|
||||
* ```
|
||||
* **Notes:**
|
||||
*
|
||||
*
|
||||
* - Disabled operations will not be added to attached models.
|
||||
* - Disabling the remoting for a method only affects client access (it will still be available from server models).
|
||||
* - Data sources must enable / disable operations before attaching or creating models.
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
var should = require('./init.js');
|
||||
|
||||
var jdb = require('../');
|
||||
var DataSource = jdb.DataSource;
|
||||
|
||||
var ds, Item, Variant;
|
||||
describe('Datasource-specific field types for foreign keys', function () {
|
||||
before(function () {
|
||||
ds = new DataSource('memory');
|
||||
Item = ds.define('Item', {
|
||||
"myProp": {
|
||||
"type": "string",
|
||||
"memory": {
|
||||
"dataType": "string"
|
||||
}
|
||||
}
|
||||
});
|
||||
Variant = ds.define('Variant', {}, {
|
||||
relations: {
|
||||
"item": {
|
||||
"type": "belongsTo",
|
||||
"as": "item",
|
||||
"model": "Item",
|
||||
"foreignKey": "myProp"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should create foreign key with database-specific field type', function (done) {
|
||||
var VariantDefinition = ds.getModelDefinition('Variant');
|
||||
should.exist(VariantDefinition);
|
||||
should.exist(VariantDefinition.properties.myProp.memory);
|
||||
should.exist(VariantDefinition.properties.myProp.memory.dataType);
|
||||
VariantDefinition.properties.myProp.memory.dataType.should.be.equal("string");
|
||||
done();
|
||||
});
|
||||
})
|
||||
;
|
Loading…
Reference in New Issue