diff --git a/lib/datasource.js b/lib/datasource.js index 2a8b7b35..bedd4ee8 100644 --- a/lib/datasource.js +++ b/lib/datasource.js @@ -886,11 +886,20 @@ DataSource.prototype.autoupdate = function (models, cb) { */ DataSource.prototype.discoverModelDefinitions = function (options, cb) { this.freeze(); + + if (cb === undefined && typeof options === 'function') { + cb = options; + options = {}; + } + options = options || {}; + cb = cb || utils.createPromiseCallback(); + if (this.connector.discoverModelDefinitions) { this.connector.discoverModelDefinitions(options, cb); } else if (cb) { process.nextTick(cb); } + return cb.promise; }; /** @@ -934,11 +943,20 @@ DataSource.prototype.discoverModelDefinitionsSync = function (options) { */ DataSource.prototype.discoverModelProperties = function (modelName, options, cb) { this.freeze(); + + if (cb === undefined && typeof options === 'function') { + cb = options; + options = {}; + } + options = options || {}; + cb = cb || utils.createPromiseCallback(); + if (this.connector.discoverModelProperties) { this.connector.discoverModelProperties(modelName, options, cb); } else if (cb) { process.nextTick(cb); } + return cb.promise; }; /** @@ -974,11 +992,20 @@ DataSource.prototype.discoverModelPropertiesSync = function (modelName, options) */ DataSource.prototype.discoverPrimaryKeys = function (modelName, options, cb) { this.freeze(); + + if (cb === undefined && typeof options === 'function') { + cb = options; + options = {}; + } + options = options || {}; + cb = cb || utils.createPromiseCallback(); + if (this.connector.discoverPrimaryKeys) { this.connector.discoverPrimaryKeys(modelName, options, cb); } else if (cb) { process.nextTick(cb); } + return cb.promise; }; /** @@ -1021,11 +1048,20 @@ DataSource.prototype.discoverPrimaryKeysSync = function (modelName, options) { */ DataSource.prototype.discoverForeignKeys = function (modelName, options, cb) { this.freeze(); + + if (cb === undefined && typeof options === 'function') { + cb = options; + options = {}; + } + options = options || {}; + cb = cb || utils.createPromiseCallback(); + if (this.connector.discoverForeignKeys) { this.connector.discoverForeignKeys(modelName, options, cb); } else if (cb) { process.nextTick(cb); } + return cb.promise; }; /** @@ -1068,11 +1104,20 @@ DataSource.prototype.discoverForeignKeysSync = function (modelName, options) { */ DataSource.prototype.discoverExportedForeignKeys = function (modelName, options, cb) { this.freeze(); + + if (cb === undefined && typeof options === 'function') { + cb = options; + options = {}; + } + options = options || {}; + cb = cb || utils.createPromiseCallback(); + if (this.connector.discoverExportedForeignKeys) { this.connector.discoverExportedForeignKeys(modelName, options, cb); } else if (cb) { process.nextTick(cb); } + return cb.promise; }; /** @@ -1177,6 +1222,8 @@ DataSource.prototype.discoverSchema = function (modelName, options, cb) { options.visited = {}; options.relations = false; + cb = cb || utils.createPromiseCallback(); + this.discoverSchemas(modelName, options, function (err, schemas) { if (err) { cb && cb(err, schemas); @@ -1187,6 +1234,7 @@ DataSource.prototype.discoverSchema = function (modelName, options, cb) { return; } }); + return cb.promise; }; /** @@ -1208,6 +1256,8 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) { options = {}; } + cb = cb || utils.createPromiseCallback(); + var self = this; var dbType = this.connector.name || this.name; @@ -1233,7 +1283,8 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) { if (this.connector.discoverSchemas) { // Delegate to the connector implementation - return this.connector.discoverSchemas(modelName, options, cb); + this.connector.discoverSchemas(modelName, options, cb); + return cb.promise; } var tasks = [ @@ -1248,14 +1299,14 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) { async.parallel(tasks, function (err, results) { if (err) { - cb && cb(err); - return; + cb(err); + return cb.promise; } var columns = results[0]; if (!columns || columns.length === 0) { - cb && cb(); - return; + cb(); + return cb.promise; } // Handle primary keys @@ -1358,7 +1409,7 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) { } if (Object.keys(otherTables).length === 0) { - cb && cb(null, options.visited); + cb(null, options.visited); } else { var moreTasks = []; for (var t in otherTables) { @@ -1375,10 +1426,11 @@ DataSource.prototype.discoverSchemas = function (modelName, options, cb) { } async.parallel(moreTasks, function (err, results) { var result = results && results[0]; - cb && cb(err, result); + cb(err, result); }); } }); + return cb.promise; }; /** diff --git a/test/discovery.test.js b/test/discovery.test.js index 23ddc361..26e5056c 100644 --- a/test/discovery.test.js +++ b/test/discovery.test.js @@ -127,4 +127,478 @@ describe('Memory connector with mocked discovery', function() { done(); }); }); + + it('should callback function, passed as options parameter', + function(done) { + var models = { + inventory: { + product: {type: 'string'}, + location: {type: 'string'} + } + }; + ds.connector.discoverSchemas = function(modelName, options, cb) { + process.nextTick(function() { + cb(null, models); + }); + }; + + var options = function(err, schemas) { + if (err) return done(err); + schemas.should.be.eql(models); + done(); + }; + + ds.discoverSchemas('INVENTORY', options); + }); + + it('should discover schemas using `discoverSchemas` - promise variant', + function(done) { + ds.connector.discoverSchemas = null; + ds.discoverSchemas('INVENTORY', {}) + .then(function(schemas) { + 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(); + }) + .catch(function(err) { + done(err); + }); + }); + + describe('discoverSchema', function(){ + var models; + var schema; + before(function() { + schema = { + name: 'Inventory', + options: { + idInjection: false, + memory: { schema: 'STRONGLOOP', table: 'INVENTORY' } + }, + properties: { + available: { + length: null, + memory: { + columnName: 'AVAILABLE', + dataLength: null, + dataPrecision: 10, + dataScale: 0, + dataType: 'int', + nullable: 1 + }, + precision: 10, + required: false, + scale: 0, + type: undefined + }, + locationId: { + length: 20, + memory: { + columnName: 'LOCATION_ID', + dataLength: 20, + dataPrecision: null, + dataScale: null, + dataType: 'varchar', + nullable: 0 + }, + precision: null, + required: true, + scale: null, + type: undefined + }, + productId: { + length: 20, + memory: { + columnName: 'PRODUCT_ID', + dataLength: 20, + dataPrecision: null, + dataScale: null, + dataType: 'varchar', + nullable: 0 + }, + precision: null, + required: true, + scale: null, + type: undefined + }, + total: { + length: null, + memory: { + columnName: 'TOTAL', + dataLength: null, + dataPrecision: 10, + dataScale: 0, + dataType: 'int', + nullable: 1 + }, + precision: 10, + required: false, + scale: 0, + type: undefined + } + } + } ; + }); + + it('should discover schema using `discoverSchema`', function(done) { + ds.discoverSchema('INVENTORY', {}, function(err, schemas) { + if (err) return done(err); + schemas.should.be.eql(schema); + done(); + }); + }); + + it('should callback function, passed as options parameter', function(done) { + var options = function(err, schemas) { + if (err) return done(err); + schemas.should.be.eql(schema); + done(); + }; + + ds.discoverSchema('INVENTORY', options); + }); + + it('should discover schema using `discoverSchema` - promise variant', function(done) { + ds.discoverSchema('INVENTORY', {}) + .then(function(schemas) { + schemas.should.be.eql(schema); + done(); + }) + .catch(function(err){ + done(err); + }); + }); + }); +}); + +describe('discoverModelDefinitions', 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.connector.discoverModelDefinitions = function(options, cb) { + process.nextTick(function() { + cb(null, models); + }); + }; + }); + + it('should discover model using `discoverModelDefinitions`', function(done) { + ds.discoverModelDefinitions({}, function(err, schemas) { + if (err) return done(err); + + var tableNames = schemas.map(function(s) { + return s.name; + }); + + tableNames.should.be.eql( + ["CUSTOMER", "INVENTORY", "LOCATION"] + ); + done(); + }); + }); + + it('should callback function, passed as options parameter', function(done) { + var options = function(err, schemas) { + if (err) return done(err); + + var tableNames = schemas.map(function(s) { + return s.name; + }); + + tableNames.should.be.eql( + ["CUSTOMER", "INVENTORY", "LOCATION"] + ); + done(); + }; + + ds.discoverModelDefinitions(options); + }); + + it('should discover model using `discoverModelDefinitions` - promise variant', function(done) { + ds.discoverModelDefinitions({}) + .then(function(schemas) { + var tableNames = schemas.map(function(s) { + return s.name; + }); + + tableNames.should.be.eql( + ["CUSTOMER", "INVENTORY", "LOCATION"] + ); + done(); + }) + .catch(function(err){ + done(err); + }); + }); +}); + +describe('discoverModelProperties', function(){ + var ds; + var modelProperties; + before(function(){ + ds = new DataSource({connector: 'memory'}); + + 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.connector.discoverModelProperties = function(modelName, options, cb) { + process.nextTick(function() { + cb(null, modelProperties); + }); + }; + }); + + it('should callback function, passed as options parameter', function(done) { + var options = function(err, schemas) { + if (err) return done(err); + + schemas.should.be.eql(modelProperties); + done(); + }; + + ds.discoverModelProperties('INVENTORY', options); + }); + + it('should discover model metadata using `discoverModelProperties`', function(done) { + ds.discoverModelProperties('INVENTORY', {}, function(err, schemas) { + if (err) return done(err); + + schemas.should.be.eql(modelProperties); + done(); + }); + }); + + it('should discover model metadata using `discoverModelProperties` - promise variant', function(done) { + ds.discoverModelProperties('INVENTORY', {}) + .then(function(schemas) { + schemas.should.be.eql(modelProperties); + done(); + }) + .catch(function(err){ + done(err); + }); + }); +}); + +describe('discoverPrimaryKeys', function(){ + var ds; + var modelProperties; + before(function(){ + ds = new DataSource({connector: 'memory'}); + + primaryKeys = [ + { + owner: 'STRONGLOOP', + tableName: 'INVENTORY', + columnName: 'PRODUCT_ID', + keySeq: 1, + pkName: 'ID_PK' + }, + { + owner: 'STRONGLOOP', + tableName: 'INVENTORY', + columnName: 'LOCATION_ID', + keySeq: 2, + pkName: 'ID_PK' + }]; + + ds.connector.discoverPrimaryKeys = function(modelName, options, cb) { + process.nextTick(function() { + cb(null, primaryKeys); + }); + }; + }); + + it('should discover primary key definitions using `discoverPrimaryKeys`', function(done) { + ds.discoverPrimaryKeys('INVENTORY', {}, function(err, modelPrimaryKeys) { + if (err) return done(err); + + modelPrimaryKeys.should.be.eql(primaryKeys); + done(); + }); + }); + + it('should callback function, passed as options parameter', function(done) { + var options = function(err, modelPrimaryKeys) { + if (err) return done(err); + + modelPrimaryKeys.should.be.eql(primaryKeys); + done(); + }; + ds.discoverPrimaryKeys('INVENTORY', options); + }); + + it('should discover primary key definitions using `discoverPrimaryKeys` - promise variant', function(done) { + ds.discoverPrimaryKeys('INVENTORY', {}) + .then(function(modelPrimaryKeys) { + modelPrimaryKeys.should.be.eql(primaryKeys); + done(); + }) + .catch(function(err){ + done(err); + }); + }); +}); + +describe('discoverForeignKeys', function(){ + var ds; + var modelProperties; + before(function(){ + ds = new DataSource({connector: 'memory'}); + + foreignKeys = [{ + fkOwner: 'STRONGLOOP', + fkName: 'PRODUCT_FK', + fkTableName: 'INVENTORY', + fkColumnName: 'PRODUCT_ID', + keySeq: 1, + pkOwner: 'STRONGLOOP', + pkName: 'PRODUCT_PK', + pkTableName: 'PRODUCT', + pkColumnName: 'ID' + }]; + + ds.connector.discoverForeignKeys = function(modelName, options, cb) { + process.nextTick(function() { + cb(null, foreignKeys); + }); + }; + }); + + it('should discover foreign key definitions using `discoverForeignKeys`', function(done) { + ds.discoverForeignKeys('INVENTORY', {}, function(err, modelForeignKeys) { + if (err) return done(err); + + modelForeignKeys.should.be.eql(foreignKeys); + done(); + }); + }); + + it('should callback function, passed as options parameter', function(done) { + var options = function(err, modelForeignKeys) { + if (err) return done(err); + + modelForeignKeys.should.be.eql(foreignKeys); + done(); + }; + + ds.discoverForeignKeys('INVENTORY', options); + }); + + it('should discover foreign key definitions using `discoverForeignKeys` - promise variant', function(done) { + ds.discoverForeignKeys('INVENTORY', {}) + .then(function(modelForeignKeys) { + modelForeignKeys.should.be.eql(foreignKeys); + done(); + }) + .catch(function(err){ + done(err); + }); + }); +}); + +describe('discoverExportedForeignKeys', function(){ + var ds; + var modelProperties; + before(function(){ + ds = new DataSource({connector: 'memory'}); + + exportedForeignKeys = [{ + fkName: 'PRODUCT_FK', + fkOwner: 'STRONGLOOP', + fkTableName: 'INVENTORY', + fkColumnName: 'PRODUCT_ID', + keySeq: 1, + pkName: 'PRODUCT_PK', + pkOwner: 'STRONGLOOP', + pkTableName: 'PRODUCT', + pkColumnName: 'ID' + }]; + + ds.connector.discoverExportedForeignKeys = function(modelName, options, cb) { + process.nextTick(function() { + cb(null, exportedForeignKeys); + }); + }; + }); + + it('should discover foreign key definitions using `discoverExportedForeignKeys`', function(done) { + ds.discoverExportedForeignKeys('INVENTORY', {}, function(err, modelForeignKeys) { + if (err) return done(err); + + modelForeignKeys.should.be.eql(exportedForeignKeys); + done(); + }); + }); + + it('should callback function, passed as options parameter', function(done) { + var options = function(err, modelForeignKeys) { + if (err) return done(err); + + modelForeignKeys.should.be.eql(exportedForeignKeys); + done(); + }; + + ds.discoverExportedForeignKeys('INVENTORY', options); + }); + + it('should discover foreign key definitions using `discoverExportedForeignKeys` - promise variant', function(done) { + ds.discoverExportedForeignKeys('INVENTORY', {}) + .then(function(modelForeignKeys) { + modelForeignKeys.should.be.eql(exportedForeignKeys); + done(); + }) + .catch(function(err){ + done(err); + }); + }); });