Allow custom name mapping for discovered models

This commit is contained in:
Raymond Feng 2015-04-22 08:42:47 -07:00
parent 2bdcce0d96
commit 1e70678fa7
2 changed files with 181 additions and 18 deletions

View File

@ -1190,7 +1190,32 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
}
var self = this;
var schemaName = this.connector.name || this.name;
var dbType = this.connector.name || this.name;
var nameMapper;
if (options.nameMapper === null) {
// No mapping
nameMapper = function(type, name) {
return name;
};
} else if (typeof options.nameMapper === 'function') {
// Custom name mapper
nameMapper = options.nameMapper;
} else {
// Default name mapper
nameMapper = function mapName(type, name) {
if (type === 'table' || type === 'model') {
return fromDBName(name, false);
} else {
return fromDBName(name, true);
}
};
}
if (this.connector.discoverSchemas) {
// Delegate to the connector implementation
return this.connector.discoverSchemas(modelName, options, cb);
}
var tasks = [
this.discoverModelProperties.bind(this, modelName, options),
@ -1215,7 +1240,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
}
// Handle primary keys
var primaryKeys = results[1];
var primaryKeys = results[1] || [];
var pks = {};
primaryKeys.forEach(function (pk) {
pks[pk.columnName] = pk.keySeq;
@ -1226,14 +1251,14 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
}
var schema = {
name: fromDBName(modelName, false),
name: nameMapper('table', modelName),
options: {
idInjection: false // DO NOT add id property
},
properties: {}
};
schema.options[schemaName] = {
schema.options[dbType] = {
schema: columns[0].owner,
table: modelName
};
@ -1241,7 +1266,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
columns.forEach(function (item) {
var i = item;
var propName = fromDBName(item.columnName, true);
var propName = nameMapper('column', item.columnName);
schema.properties[propName] = {
type: item.type,
required: (item.nullable === 'N' || item.nullable === 'NO'
@ -1254,7 +1279,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
if (pks[item.columnName]) {
schema.properties[propName].id = pks[item.columnName];
}
schema.properties[propName][schemaName] = {
schema.properties[propName][dbType] = {
columnName: i.columnName,
dataType: i.dataType,
dataLength: i.dataLength,
@ -1278,7 +1303,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
if (followingRelations) {
// Handle foreign keys
var fks = {};
var foreignKeys = results[2];
var foreignKeys = results[2] || [];
foreignKeys.forEach(function (fk) {
var fkInfo = {
keySeq: fk.keySeq,
@ -1299,11 +1324,11 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
schema.options.relations = {};
foreignKeys.forEach(function (fk) {
var propName = fromDBName(fk.pkTableName, true);
var propName = nameMapper('column', fk.pkTableName);
schema.options.relations[propName] = {
model: fromDBName(fk.pkTableName, false),
model: nameMapper('table', fk.pkTableName),
type: 'belongsTo',
foreignKey: fromDBName(fk.fkColumnName, true)
foreignKey: nameMapper('column', fk.fkColumnName)
};
var key = fk.pkOwner + '.' + fk.pkTableName;
@ -1349,13 +1374,21 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) {
*/
DataSource.prototype.discoverSchemasSync = function (modelName, options) {
var self = this;
var schemaName = this.name || this.connector.name;
var dbType = this.name || this.connector.name;
var columns = this.discoverModelPropertiesSync(modelName, options);
if (!columns || columns.length === 0) {
return [];
}
var nameMapper = options.nameMapper || function mapName(type, name) {
if (type === 'table' || type === 'model') {
return fromDBName(name, false);
} else {
return fromDBName(name, true);
}
};
// Handle primary keys
var primaryKeys = this.discoverPrimaryKeysSync(modelName, options);
var pks = {};
@ -1368,14 +1401,14 @@ DataSource.prototype.discoverSchemasSync = function (modelName, options) {
}
var schema = {
name: fromDBName(modelName, false),
name: nameMapper('table', modelName),
options: {
idInjection: false // DO NOT add id property
},
properties: {}
};
schema.options[schemaName] = {
schema.options[dbType] = {
schema: columns.length > 0 && columns[0].owner,
table: modelName
};
@ -1383,7 +1416,7 @@ DataSource.prototype.discoverSchemasSync = function (modelName, options) {
columns.forEach(function (item) {
var i = item;
var propName = fromDBName(item.columnName, true);
var propName = nameMapper('column', item.columnName);
schema.properties[propName] = {
type: item.type,
required: (item.nullable === 'N'),
@ -1395,7 +1428,7 @@ DataSource.prototype.discoverSchemasSync = function (modelName, options) {
if (pks[item.columnName]) {
schema.properties[propName].id = pks[item.columnName];
}
schema.properties[propName][schemaName] = {
schema.properties[propName][dbType] = {
columnName: i.columnName,
dataType: i.dataType,
dataLength: i.dataLength,
@ -1441,11 +1474,11 @@ DataSource.prototype.discoverSchemasSync = function (modelName, options) {
schema.options.relations = {};
foreignKeys.forEach(function (fk) {
var propName = fromDBName(fk.pkTableName, true);
var propName = nameMapper('column', fk.pkTableName);
schema.options.relations[propName] = {
model: fromDBName(fk.pkTableName, false),
model: nameMapper('table', fk.pkTableName),
type: 'belongsTo',
foreignKey: fromDBName(fk.fkColumnName, true)
foreignKey: nameMapper('column', fk.fkColumnName)
};
var key = fk.pkOwner + '.' + fk.pkTableName;

130
test/discovery.test.js Normal file
View File

@ -0,0 +1,130 @@
var jdb = require('../');
var DataSource = jdb.DataSource;
var should = require('./init.js');
describe('Memory connector with mocked discovery', function() {
var ds;
before(function() {
ds = new DataSource({connector: 'memory'});
var models = [{type: 'table', name: 'CUSTOMER', owner: 'STRONGLOOP'},
{type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP'},
{type: 'table', name: 'LOCATION', owner: 'STRONGLOOP'}];
ds.discoverModelDefinitions = function(options, cb) {
process.nextTick(function() {
cb(null, models);
});
};
var modelProperties = [{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'PRODUCT_ID',
dataType: 'varchar',
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'LOCATION_ID',
dataType: 'varchar',
dataLength: 20,
dataPrecision: null,
dataScale: null,
nullable: 0
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'AVAILABLE',
dataType: 'int',
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1
},
{
owner: 'STRONGLOOP',
tableName: 'INVENTORY',
columnName: 'TOTAL',
dataType: 'int',
dataLength: null,
dataPrecision: 10,
dataScale: 0,
nullable: 1
}];
ds.discoverModelProperties = function(modelName, options, cb) {
process.nextTick(function() {
cb(null, modelProperties);
});
};
});
it('should convert table/column names to camel cases', function(done) {
ds.discoverSchemas('INVENTORY', {}, function(err, schemas) {
if (err) return done(err);
schemas.should.have.property('STRONGLOOP.INVENTORY');
var s = schemas['STRONGLOOP.INVENTORY'];
s.name.should.be.eql('Inventory');
Object.keys(s.properties).should.be.eql(
['productId', 'locationId', 'available', 'total']);
done();
});
});
it('should convert table/column names with custom mapper', function(done) {
ds.discoverSchemas('INVENTORY', {
nameMapper: function(type, name) {
// Convert all names to lower case
return name.toLowerCase();
}
}, function(err, schemas) {
if (err) return done(err);
schemas.should.have.property('STRONGLOOP.INVENTORY');
var s = schemas['STRONGLOOP.INVENTORY'];
s.name.should.be.eql('inventory');
Object.keys(s.properties).should.be.eql(
['product_id', 'location_id', 'available', 'total']);
done();
});
});
it('should not convert table/column names with null custom mapper',
function(done) {
ds.discoverSchemas('INVENTORY', {nameMapper: null}, function(err, schemas) {
if (err) return done(err);
schemas.should.have.property('STRONGLOOP.INVENTORY');
var s = schemas['STRONGLOOP.INVENTORY'];
s.name.should.be.eql('INVENTORY');
Object.keys(s.properties).should.be.eql(
['PRODUCT_ID', 'LOCATION_ID', 'AVAILABLE', 'TOTAL']);
done();
});
});
it('should honor connector\'s discoverSchemas implementation',
function(done) {
var models = {
inventory: {
product: {type: 'string'},
location: {type: 'string'}
}
};
ds.connector.discoverSchemas = function(modelName, options, cb) {
process.nextTick(function() {
cb(null, models);
});
};
ds.discoverSchemas('INVENTORY', {nameMapper: null}, function(err, schemas) {
if (err) return done(err);
schemas.should.be.eql(models);
done();
});
});
});