diff --git a/README.md b/README.md
index da3f24a..519f1f9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-## Loopback MySQL Connector
+## loopback-connector-mysql
-MySQL connector for LoopBack Data Source Juggler.
+MySQL connector for [LoopBack Data Source Juggler](http://docs.strongloop.com/loopback-datasource-juggler/).
## Usage
@@ -24,51 +24,48 @@ To use it you need `loopback-datasource-juggler`.
```javascript
var DataSource = require('loopback-datasource-juggler').DataSource;
var dataSource = new DataSource('mysql', {
- database: 'myapp_test',
- username: 'root'
+ database: 'mydb',
+ username: 'myuser',
+ password: 'mypass'
});
```
- You can optionally pass a few additional parameters supported by `node-mysql`,
+ You can optionally pass a few additional parameters supported by [`node-mysql`](https://github.com/felixge/node-mysql),
most particularly `password` and `collation`. `Collation` currently defaults
to `utf8_general_ci`. The `collation` value will also be used to derive the
connection charset.
-## Running tests
-
- npm test
## Using the `dataType` field/column option with MySQL
-The loopback-datasource-juggler MySQL adapter now supports using the `dataType`
-column/property attribute to specify what MySQL column type is used for many
-loopback-datasource-juggler types.
+The loopback-datasource-juggler MySQL connector now supports using the `dataType` column/property attribute to specify
+what MySQL column type is used for many loopback-datasource-juggler types.
The following type-dataType combinations are supported:
-*
Number
- * integer
- * tinyint
- * smallint
- * mediumint
- * int
- * bigint
+- Number
+ - integer
+ - tinyint
+ - smallint
+ - mediumint
+ - int
+ - bigint
Use the `limit` option to alter the display width.
Example:
`{ count : { type: Number, dataType: 'smallInt' }}`
- * floating point types
- * float
- * double
+ - floating point types
+ - float
+ - double
Use the `precision` and `scale` options to specify custom precision. Default is (16,8).
Example:
`{ average : { type: Number, dataType: 'float', precision: 20, scale: 4 }}`
- * fixed-point exact value types
- * decimal
- * numeric
+ - fixed-point exact value types
+ - decimal
+ - numeric
Use the `precision` and `scale` options to specify custom precision. Default is (9,2).
@@ -77,13 +74,13 @@ The following type-dataType combinations are supported:
Example:
`{ stdDev : { type: Number, dataType: 'decimal', precision: 12, scale: 8 }}`
-* String / DataSource.Text / DataSource.JSON
- * varchar
- * char
- * text
- * mediumtext
- * tinytext
- * longtext
+- String / DataSource.Text / DataSource.JSON
+ - varchar
+ - char
+ - text
+ - mediumtext
+ - tinytext
+ - longtext
Example:
`{ userName : { type: String, dataType: 'char', limit: 24 }}`
@@ -91,14 +88,14 @@ The following type-dataType combinations are supported:
Example:
`{ biography : { type: String, dataType: 'longtext' }}`
-* Date
- * datetime
- * timestamp
+- Date
+ - datetime
+ - timestamp
Example:
`{ startTime : { type: Date, dataType: 'timestamp' }}`
-* Enum
+* Enum
Enums are special.
Create an Enum using Enum factory:
@@ -110,37 +107,165 @@ The following type-dataType combinations are supported:
MOOD('sad'); // 'sad'
```
- * `{ mood: { type: MOOD }}`
- * `{ choice: { type: dataSource.EnumFactory('yes', 'no', 'maybe'), null: false }}`
+ - `{ mood: { type: MOOD }}`
+ - `{ choice: { type: dataSource.EnumFactory('yes', 'no', 'maybe'), null: false }}`
+## Discovering Models
+
+MySQL data sources allow you to discover model definition information from existing mysql databases. See the following APIs:
+
+ - [dataSource.discoverModelDefinitions([username], fn)](https://github.com/strongloop/loopback#datasourcediscovermodeldefinitionsusername-fn)
+ - [dataSource.discoverSchema([owner], name, fn)](https://github.com/strongloop/loopback#datasourcediscoverschemaowner-name-fn)
+
+### Asynchronous APIs for discovery
+
+* MySQL.prototype.discoverModelDefinitions = function (options, cb)
+ - options:
+ - all: {Boolean} To include tables/views from all schemas/owners
+ - owner/schema: {String} The schema/owner name
+ - views: {Boolean} To include views
+ - cb:
+ - Get a list of table/view names, for example:
+
+ {type: 'table', name: 'INVENTORY', owner: 'STRONGLOOP' }
+ {type: 'table', name: 'LOCATION', owner: 'STRONGLOOP' }
+ {type: 'view', name: 'INVENTORY_VIEW', owner: 'STRONGLOOP' }
+
+
+* MySQL.prototype.discoverModelProperties = function (table, options, cb)
+ - table: {String} The name of a table or view
+ - options:
+ - owner/schema: {String} The schema/owner name
+ - cb:
+ - Get a list of model property definitions, for example:
+
+ { owner: 'STRONGLOOP',
+ tableName: 'PRODUCT',
+ columnName: 'ID',
+ dataType: 'VARCHAR2',
+ dataLength: 20,
+ nullable: 'N',
+ type: 'String' }
+ { owner: 'STRONGLOOP',
+ tableName: 'PRODUCT',
+ columnName: 'NAME',
+ dataType: 'VARCHAR2',
+ dataLength: 64,
+ nullable: 'Y',
+ type: 'String' }
+
+
+* MySQL.prototype.discoverPrimaryKeys= function(table, options, cb)
+ - table: {String} The name of a table or view
+ - options:
+ - owner/schema: {String} The schema/owner name
+ - cb:
+ - Get a list of primary key definitions, for example:
+
+ { owner: 'STRONGLOOP',
+ tableName: 'INVENTORY',
+ columnName: 'PRODUCT_ID',
+ keySeq: 1,
+ pkName: 'ID_PK' }
+ { owner: 'STRONGLOOP',
+ tableName: 'INVENTORY',
+ columnName: 'LOCATION_ID',
+ keySeq: 2,
+ pkName: 'ID_PK' }
+
+* MySQL.prototype.discoverForeignKeys= function(table, options, cb)
+ - table: {String} The name of a table or view
+ - options:
+ - owner/schema: {String} The schema/owner name
+ - cb:
+ - Get a list of foreign key definitions, for example:
+
+ { fkOwner: 'STRONGLOOP',
+ fkName: 'PRODUCT_FK',
+ fkTableName: 'INVENTORY',
+ fkColumnName: 'PRODUCT_ID',
+ keySeq: 1,
+ pkOwner: 'STRONGLOOP',
+ pkName: 'PRODUCT_PK',
+ pkTableName: 'PRODUCT',
+ pkColumnName: 'ID' }
+
+
+* MySQL.prototype.discoverExportedForeignKeys= function(table, options, cb)
+
+ - table: {String} The name of a table or view
+ - options:
+ - owner/schema: {String} The schema/owner name
+ - cb:
+ - Get a list of foreign key definitions that reference the primary key of the given table, for example:
+
+ { fkName: 'PRODUCT_FK',
+ fkOwner: 'STRONGLOOP',
+ fkTableName: 'INVENTORY',
+ fkColumnName: 'PRODUCT_ID',
+ keySeq: 1,
+ pkName: 'PRODUCT_PK',
+ pkOwner: 'STRONGLOOP',
+ pkTableName: 'PRODUCT',
+ pkColumnName: 'ID' }
+
+
+### Synchronous APIs for discovery
+
+* MySQL.prototype.discoverModelDefinitionsSync = function (options)
+* MySQL.prototype.discoverModelPropertiesSync = function (table, options)
+* MySQL.prototype.discoverPrimaryKeysSync= function(table, options)
+* MySQL.prototype.discoverForeignKeysSync= function(table, options)
+* MySQL.prototype.discoverExportedForeignKeysSync= function(table, options)
+
+### Discover/build/try the models
+
+The following example uses `discoverAndBuildModels` to discover, build and try the models:
+
+ dataSource.discoverAndBuildModels('INVENTORY', { owner: 'STRONGLOOP', visited: {}, associations: true},
+ function (err, models) {
+ // Show records from the models
+ for(var m in models) {
+ models[m].all(show);
+ };
+
+ // Find one record for inventory
+ models.Inventory.findOne({}, function(err, inv) {
+ console.log("\nInventory: ", inv);
+ // Follow the foreign key to navigate to the product
+ inv.product(function(err, prod) {
+ console.log("\nProduct: ", prod);
+ console.log("\n ------------- ");
+ });
+ });
+ }
+
## License
-Please see LICENSE.
+[The MIT license](LICENSE).
The project was initially forked from [mysql-adapter](https://github.com/jugglingdb/mysql-adapter)
which carries the following copyright and permission notices:
-```text
-Copyright (C) 2012 by Anatoliy Chakkaev
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-```
+ Copyright (C) 2012 by Anatoliy Chakkaev
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
-```text
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-```
diff --git a/docs.json b/docs.json
new file mode 100644
index 0000000..2ada8c2
--- /dev/null
+++ b/docs.json
@@ -0,0 +1,10 @@
+{
+"content": [
+ "README.md",
+ {"title": "LoopBack MySQL Connector API", "depth": 2},
+ "lib/mysql.js",
+ "lib/discovery.js"
+ ],
+ "codeSectionDepth": 3
+}
+
diff --git a/lib/discovery.js b/lib/discovery.js
index a42542a..56d8591 100644
--- a/lib/discovery.js
+++ b/lib/discovery.js
@@ -18,7 +18,7 @@ function mixinDiscovery(MySQL) {
return sql + limit;
}
- /**
+ /*!
* Build sql for listing tables
* @param options {all: for all owners, owner: for a given owner}
* @returns {string} The sql statement
@@ -41,7 +41,7 @@ function mixinDiscovery(MySQL) {
return sqlTables;
}
- /**
+ /*!
* Build sql for listing views
* @param options {all: for all owners, owner: for a given owner}
* @returns {string} The sql statement
@@ -70,7 +70,10 @@ function mixinDiscovery(MySQL) {
}
/**
- * Discover the models
+ * Discover model definitions
+ *
+ * @param {Object} options Options for discovery
+ * @param {Function} [cb] The callback function
*/
MySQL.prototype.discoverModelDefinitions = function (options, cb) {
if (!cb && typeof options === 'function') {
@@ -101,10 +104,11 @@ function mixinDiscovery(MySQL) {
cb(err, merged);
}
});
- }
+ };
/**
* Discover the tables/views synchronously
+ * @param {Object} options The options for discovery
*/
MySQL.prototype.discoverModelDefinitionsSync = function (options) {
options = options || {};
@@ -116,9 +120,9 @@ function mixinDiscovery(MySQL) {
tables = tables.concat(views);
}
return tables;
- }
+ };
- /**
+ /*!
* Normalize the arguments
* @param table string, required
* @param options object, optional
@@ -144,7 +148,7 @@ function mixinDiscovery(MySQL) {
};
}
- /**
+ /*!
* Build the sql statement to query columns for a given table
* @param owner
* @param table
@@ -169,6 +173,13 @@ function mixinDiscovery(MySQL) {
return sql;
}
+ /**
+ * Discover model properties from a table
+ * @param {String} table The table name
+ * @param {Object} options The options for discovery
+ * @param {Function} [cb] The callback function
+ *
+ */
MySQL.prototype.discoverModelProperties = function (table, options, cb) {
var args = getArgs(table, options, cb);
var owner = args.owner;
@@ -188,8 +199,15 @@ function mixinDiscovery(MySQL) {
}
};
this.query(sql, callback);
- }
+ };
+ /**
+ * Discover model properties from a table synchronously
+ * @param {String} table The table name
+ * @param {Object} options The options for discovery
+ * @return {Object[]} The results
+ *
+ */
MySQL.prototype.discoverModelPropertiesSync = function (table, options) {
var args = getArgs(table, options);
var owner = args.owner;
@@ -203,9 +221,9 @@ function mixinDiscovery(MySQL) {
r.type = mysqlDataTypeToJSONType(r.dataType, r.dataLength);
});
return results;
- }
+ };
- /**
+ /*!
* Build the sql statement for querying primary keys of a given table
* @param owner
* @param table
@@ -230,9 +248,9 @@ function mixinDiscovery(MySQL) {
/**
* Discover primary keys for a given table
- * @param table
- * @param options
- * @param cb
+ * @param {String} table The table name
+ * @param {Object} options The options for discovery
+ * @param {Function} [cb] The callback function
*/
MySQL.prototype.discoverPrimaryKeys = function (table, options, cb) {
var args = getArgs(table, options, cb);
@@ -243,13 +261,13 @@ function mixinDiscovery(MySQL) {
var sql = queryForPrimaryKeys(owner, table);
this.query(sql, cb);
- }
+ };
/**
* Discover primary keys synchronously for a given table
- * @param table
- * @param options
- * @returns {*}
+ * @param {String} table
+ * @param {Object} options
+ * @returns {*} The list of primary key descriptions
*/
MySQL.prototype.discoverPrimaryKeysSync = function (table, options) {
var args = getArgs(table, options);
@@ -259,9 +277,9 @@ function mixinDiscovery(MySQL) {
var sql = queryForPrimaryKeys(owner, table);
return this.querySync(sql);
- }
+ };
- /**
+ /*!
* Build the sql statement for querying foreign keys of a given table
* @param owner
* @param table
@@ -286,10 +304,10 @@ function mixinDiscovery(MySQL) {
}
/**
- * Discover foreign kets for a given table
- * @param table
- * @param options
- * @param cb
+ * Discover foreign keys for a given table
+ * @param {String} table The table name
+ * @param {Object} options The options for discovery
+ * @param {Function} [cb] The callback function
*/
MySQL.prototype.discoverForeignKeys = function (table, options, cb) {
var args = getArgs(table, options, cb);
@@ -300,13 +318,13 @@ function mixinDiscovery(MySQL) {
var sql = queryForeignKeys(owner, table);
this.query(sql, cb);
- }
+ };
/**
* Discover foreign keys synchronously for a given table
- * @param owner
- * @param options
- * @returns {*}
+ * @param {String} table The table name
+ * @param {Object} options The options for discovery
+ * @return {Object[]} The results
*/
MySQL.prototype.discoverForeignKeysSync = function (table, options) {
var args = getArgs(table, options);
@@ -316,9 +334,9 @@ function mixinDiscovery(MySQL) {
var sql = queryForeignKeys(owner, table);
return this.querySync(sql);
- }
+ };
- /**
+ /*!
* Retrieves a description of the foreign key columns that reference the given table's primary key columns (the foreign keys exported by a table).
* They are ordered by fkTableOwner, fkTableName, and keySeq.
* @param owner
@@ -354,6 +372,12 @@ function mixinDiscovery(MySQL) {
return sql;
}
+ /**
+ * Discover foreign keys that reference to the primary key of this table
+ * @param {String} table The table name
+ * @param {Object} options The options for discovery
+ * @param {Function} [cb] The callback function
+ */
MySQL.prototype.discoverExportedForeignKeys = function (table, options, cb) {
var args = getArgs(table, options, cb);
var owner = args.owner;
@@ -363,12 +387,12 @@ function mixinDiscovery(MySQL) {
var sql = queryExportedForeignKeys(owner, table);
this.query(sql, cb);
- }
+ };
/**
* Discover foreign keys synchronously for a given table
- * @param owner
- * @param table
+ * @param {String} owner The DB owner/schema name
+ * @param {Object} options The options for discovery
* @returns {*}
*/
MySQL.prototype.discoverExportedForeignKeysSync = function (table, options) {
@@ -379,7 +403,7 @@ function mixinDiscovery(MySQL) {
var sql = queryExportedForeignKeys(owner, table);
return this.querySync(sql);
- }
+ };
function mysqlDataTypeToJSONType(mysqlType, dataLength) {
var type = mysqlType.toUpperCase();
diff --git a/lib/mysql.js b/lib/mysql.js
index a57f6bb..a90b983 100644
--- a/lib/mysql.js
+++ b/lib/mysql.js
@@ -1,4 +1,4 @@
-/**
+/*!
* Module dependencies
*/
var mysql = require('mysql');
@@ -6,8 +6,18 @@ var mysql = require('mysql');
var jdb = require('loopback-datasource-juggler');
var EnumFactory = require('./enumFactory').EnumFactory;
+/**
+ * @module loopback-connector-mysql
+ *
+ * Initialize the MySQL connector against the given data source
+ *
+ * @param {DataSource} dataSource The loopback-datasource-juggler dataSource
+ * @param {Function} [callback] The callback function
+ */
exports.initialize = function initializeDataSource(dataSource, callback) {
- if (!mysql) return;
+ if (!mysql) {
+ return;
+ }
var s = dataSource.settings;
@@ -69,7 +79,9 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
exports.MySQL = MySQL;
/**
- * MySQL connector
+ * @constructor
+ * Constructor for MySQL connector
+ * @param {Object} client The node-mysql client object
*/
function MySQL(client) {
this.name = 'mysql';
@@ -79,6 +91,12 @@ function MySQL(client) {
require('util').inherits(MySQL, jdb.BaseSQL);
+/**
+ * Execute the sql statement
+ *
+ * @param {String} sql The SQL statement
+ * @param {Function} [callback] The callback after the SQL statement is executed
+ */
MySQL.prototype.query = function (sql, callback) {
if (!this.dataSource.connected) {
return this.dataSource.on('connected', function () {
@@ -107,7 +125,11 @@ MySQL.prototype.query = function (sql, callback) {
};
/**
- * Must invoke callback(err, id)
+ * Create the data model in MySQL
+ *
+ * @param {String} model The model name
+ * @param {Object} data The model instance data
+ * @param {Function} [callback] The callback function
*/
MySQL.prototype.create = function (model, data, callback) {
var fields = this.toFields(model, data);
@@ -122,6 +144,13 @@ MySQL.prototype.create = function (model, data, callback) {
});
};
+/**
+ * Update if the model instance exists with the same id or create a new instance
+ *
+ * @param {String} model The model name
+ * @param {Object} data The model instance data
+ * @param {Function} [callback] The callback function
+ */
MySQL.prototype.updateOrCreate = function (model, data, callback) {
var mysql = this;
var fieldsNames = [];
@@ -182,6 +211,12 @@ function dateToMysql(val) {
}
}
+/*!
+ * Convert property name/value to a DB column
+ * @param prop
+ * @param val
+ * @returns {*}
+ */
MySQL.prototype.toDatabase = function (prop, val) {
if (val === null) return 'NULL';
if (val === undefined) return;
@@ -217,6 +252,12 @@ MySQL.prototype.toDatabase = function (prop, val) {
return this.client.escape(val.toString());
};
+/*!
+ * Convert the data from database
+ * @param model
+ * @param data
+ * @returns {*}
+ */
MySQL.prototype.fromDatabase = function (model, data) {
if (!data) return null;
var props = this._models[model].properties;
@@ -349,6 +390,13 @@ function buildLimit(limit, offset) {
return 'LIMIT ' + (offset ? (offset + ', ' + limit) : limit);
}
+/**
+ * Find matching model instances by the filter
+ *
+ * @param {String} model The model name
+ * @param {Object} filter The filter
+ * @param {Function} [callback] The callback function
+ */
MySQL.prototype.all = function all(model, filter, callback) {
// Order by id if no order is specified
filter = filter || {};
@@ -416,6 +464,11 @@ MySQL.prototype.destroyAll = function destroyAll(model, where, callback) {
}.bind(this));
};
+/**
+ * Perform autoupdate for the given models
+ * @param {String[]} [models] A model name or an array of model names. If not present, apply to all models
+ * @param {Function} [cb] The callback function
+ */
MySQL.prototype.autoupdate = function (models, cb) {
var self = this;
var wait = 0;
@@ -455,6 +508,11 @@ MySQL.prototype.autoupdate = function (models, cb) {
}
};
+/**
+ * Check if the models exist
+ * @param {String[]} [models] A model name or an array of model names. If not present, apply to all models
+ * @param {Function} [cb] The callback function
+ */
MySQL.prototype.isActual = function (cb) {
var ok = false;
var self = this;