mysql-import
Advanced tools
Comparing version 3.0.6 to 4.0.24
/** | ||
* mysql-import - v3.0.6 | ||
* mysql-import - v4.0.24 | ||
* Import .sql into a MySQL database with Node. | ||
@@ -15,63 +15,190 @@ * @author Rob Parham | ||
class importer{ | ||
constructor(settings, err_handler){ | ||
this.settings = settings; | ||
this.conn = null; | ||
this.err_handler = (e)=>{ | ||
err_handler(e); | ||
this.disconnect(); | ||
} | ||
/** | ||
* mysql-import - Importer class | ||
* @version 4.0.24 | ||
* https://github.com/Pamblam/mysql-import | ||
*/ | ||
class Importer{ | ||
/** | ||
* new Importer(settings) | ||
* @param {host, user, password[, database]} settings - login credentials | ||
*/ | ||
constructor(settings){ | ||
this._connection_settings = settings; | ||
this._conn = null; | ||
this._encoding = 'utf8'; | ||
this._imported = []; | ||
} | ||
connect(){ | ||
this.conn = this.conn || mysql.createConnection(this.settings); | ||
/** | ||
* Get an array of the imported files | ||
* @returns {Array} | ||
*/ | ||
getImported(){ | ||
return this._imported.slice(0); | ||
} | ||
disconnect(){ | ||
if(!this.conn) return; | ||
try{ | ||
this.conn.end(); | ||
}catch(e){} | ||
this.conn = null; | ||
/** | ||
* Set the encoding to be used for reading the dump files. | ||
* @param string - encoding type to be used. | ||
* @throws {Error} - if unsupported encoding type. | ||
* @returns {undefined} | ||
*/ | ||
setEncoding(encoding){ | ||
var supported_encodings = [ | ||
'utf8', | ||
'ucs2', | ||
'utf16le', | ||
'latin1', | ||
'ascii', | ||
'base64', | ||
'hex' | ||
]; | ||
if(!supported_encodings.includes(encoding)){ | ||
throw new Error("Unsupported encoding: "+encoding); | ||
} | ||
this._encoding = encoding; | ||
} | ||
getSQLFilePaths(paths){ | ||
if(!Array.isArray(paths)) paths = [paths]; | ||
var full_paths = []; | ||
for(var i=paths.length; i--;){ | ||
let exists = fs.existsSync(paths[i]); | ||
if(!exists) continue; | ||
let stat = fs.lstatSync(paths[i]); | ||
let isFile = stat.isFile(); | ||
let isDir = stat.isDirectory(); | ||
if(!isFile && !isDir) continue; | ||
if(isFile){ | ||
if(paths[i].toLowerCase().substring(paths[i].length-4) === '.sql'){ | ||
full_paths.push(path.resolve(paths[i])); | ||
/** | ||
* Set or change the database to be used | ||
* @param string - database name | ||
* @returns {Promise} | ||
*/ | ||
use(database){ | ||
return new Promise((resolve, reject)=>{ | ||
if(!this._conn){ | ||
this._connection_settings.database = database; | ||
return; | ||
} | ||
this._conn.changeUser({database}, err=>{ | ||
if (err){ | ||
reject(err); | ||
}else{ | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Import (an) .sql file(s). | ||
* @param string|array input - files or paths to scan for .sql files | ||
* @returns {Promise} | ||
*/ | ||
import(...input){ | ||
return new Promise(async (resolve, reject)=>{ | ||
try{ | ||
await this._connect(); | ||
var files = await this._getSQLFilePaths(...input); | ||
var error = null; | ||
await slowLoop(files, (file, index, next)=>{ | ||
if(error){ | ||
next(); | ||
return; | ||
} | ||
this._importSingleFile(file).then(()=>{ | ||
next(); | ||
}).catch(err=>{ | ||
error = err; | ||
next(); | ||
}); | ||
}); | ||
if(error) throw error; | ||
await this.disconnect(); | ||
resolve(); | ||
}catch(err){ | ||
reject(err); | ||
} | ||
}); | ||
}; | ||
/** | ||
* Disconnect mysql. This is done automatically, so shouldn't need to be manually called. | ||
* @param bool graceful - force close? | ||
* @returns {Promise} | ||
*/ | ||
disconnect(graceful=true){ | ||
return new Promise((resolve, reject)=>{ | ||
if(!this._conn){ | ||
resolve(); | ||
return; | ||
} | ||
if(graceful){ | ||
this._conn.end(err=>{ | ||
if(err){ | ||
reject(err); | ||
return; | ||
} | ||
this._conn = null; | ||
resolve(); | ||
}); | ||
}else{ | ||
var more_paths = fs.readdirSync(paths[i]).map(p=>path.join(paths[i], p)); | ||
full_paths.push(...this.getSQLFilePaths(more_paths)); | ||
} | ||
} | ||
return full_paths; | ||
this._conn.destroy(); | ||
resolve(); | ||
} | ||
}); | ||
} | ||
importSingleFile(filename){ | ||
return new Promise(done=>{ | ||
var queriesString = fs.readFileSync(filename, 'utf8'); | ||
var queries = new queryParser(queriesString).queries; | ||
slowLoop(queries, (q,i,d)=>{ | ||
try{ | ||
this.conn.query(q, err=>{ | ||
/* istanbul ignore next */ | ||
if (err) this.err_handler(err); | ||
else d(); | ||
//////////////////////////////////////////////////////////////////////////// | ||
// Private methods ///////////////////////////////////////////////////////// | ||
//////////////////////////////////////////////////////////////////////////// | ||
/** | ||
* Import a single .sql file into the database | ||
* @param {type} filepath | ||
* @returns {Promise} | ||
*/ | ||
_importSingleFile(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.readFile(filepath, this._encoding, (err, queriesString) => { | ||
if(err){ | ||
reject(err); | ||
return; | ||
} | ||
var queries = new queryParser(queriesString).queries; | ||
var error = null; | ||
slowLoop(queries, (query, index, next)=>{ | ||
if(error){ | ||
next(); | ||
return; | ||
} | ||
this._conn.query(query, err=>{ | ||
if (err) error = err; | ||
next(); | ||
}); | ||
}catch(e){ | ||
/* istanbul ignore next */ | ||
this.err_handler(e); | ||
}).then(()=>{ | ||
if(error){ | ||
reject(error); | ||
}else{ | ||
this._imported.push(filepath); | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Connect to the mysql server | ||
* @returns {Promise} | ||
*/ | ||
_connect(){ | ||
return new Promise((resolve, reject)=>{ | ||
if(this._conn){ | ||
resolve(this._conn); | ||
return; | ||
} | ||
var connection = mysql.createConnection(this._connection_settings); | ||
connection.connect(err=>{ | ||
if (err){ | ||
reject(err); | ||
}else{ | ||
this._conn = connection; | ||
resolve(); | ||
} | ||
}).then(()=>{ | ||
done(); | ||
}); | ||
@@ -81,39 +208,107 @@ }); | ||
import(input){ | ||
return new Promise(done=>{ | ||
this.connect(); | ||
var files = this.getSQLFilePaths(input); | ||
slowLoop(files, (f,i,d)=>{ | ||
this.importSingleFile(f).then(d); | ||
}).then(()=>{ | ||
this.disconnect(); | ||
done(); | ||
/** | ||
* Check if a file exists | ||
* @param string filepath | ||
* @returns {Promise} | ||
*/ | ||
_fileExists(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.access(filepath, fs.F_OK, err=>{ | ||
if(err){ | ||
reject(err); | ||
}else{ | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}; | ||
} | ||
/** | ||
* Get filetype information | ||
* @param string filepath | ||
* @returns {Promise} | ||
*/ | ||
_statFile(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.lstat(filepath, (err, stat)=>{ | ||
if(err){ | ||
reject(err); | ||
}else{ | ||
resolve(stat); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
/** | ||
* Read contents of a directory | ||
* @param string filepath | ||
* @returns {Promise} | ||
*/ | ||
_readDir(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.readdir(filepath, (err, files)=>{ | ||
if(err){ | ||
reject(err); | ||
}else{ | ||
resolve(files); | ||
} | ||
}); | ||
}); | ||
} | ||
importer.version = '3.0.6'; | ||
importer.config = function(settings){ | ||
const valid = settings.hasOwnProperty('host') && typeof settings.host === "string" && | ||
settings.hasOwnProperty('user') && typeof settings.user === "string" && | ||
settings.hasOwnProperty('password') && typeof settings.password === "string" && | ||
settings.hasOwnProperty('database') && typeof settings.database === "string"; | ||
/* istanbul ignore next */ | ||
if(!settings.hasOwnProperty("onerror") || typeof settings.onerror !== "function"){ | ||
settings.onerror = err=>{ throw err }; | ||
/** | ||
* Parses the input argument(s) for Importer.import into an array sql files. | ||
* @param strings|array paths | ||
* @returns {Promise} | ||
*/ | ||
_getSQLFilePaths(...paths){ | ||
return new Promise(async (resolve, reject)=>{ | ||
var full_paths = []; | ||
var error = null; | ||
paths = [].concat.apply([], paths); // flatten array of paths | ||
await slowLoop(paths, async (filepath, index, next)=>{ | ||
if(error){ | ||
next(); | ||
return; | ||
} | ||
try{ | ||
await this._fileExists(filepath); | ||
var stat = await this._statFile(filepath); | ||
if(stat.isFile()){ | ||
if(filepath.toLowerCase().substring(filepath.length-4) === '.sql'){ | ||
full_paths.push(path.resolve(filepath)); | ||
} | ||
next(); | ||
}else if(stat.isDirectory()){ | ||
var more_paths = await this._readDir(filepath); | ||
more_paths = more_paths.map(p=>path.join(filepath, p)); | ||
var sql_files = await this._getSQLFilePaths(...more_paths); | ||
full_paths.push(...sql_files); | ||
next(); | ||
}else{ | ||
next(); | ||
} | ||
}catch(err){ | ||
error = err; | ||
next(); | ||
} | ||
}); | ||
if(error){ | ||
reject(error); | ||
}else{ | ||
resolve(full_paths); | ||
} | ||
}); | ||
} | ||
} | ||
var err_handler = settings.onerror; | ||
/** | ||
* Build version number | ||
*/ | ||
Importer.version = '4.0.24'; | ||
/* istanbul ignore next */ | ||
if(!valid) return settings.onerror(new Error("Invalid host, user, password, or database parameters")); | ||
module.exports = Importer; | ||
return new importer(settings, err_handler); | ||
}; | ||
module.exports = importer; | ||
/** | ||
@@ -120,0 +315,0 @@ * Execute the loopBody function once for each item in the items array, |
@@ -8,3 +8,3 @@ { | ||
"dependencies": { | ||
"mysql": "^2.10.2" | ||
"mysql": "^2.18.1" | ||
}, | ||
@@ -14,9 +14,9 @@ "deprecated": false, | ||
"devDependencies": { | ||
"chai": "^4.1.2", | ||
"coveralls": "^3.0.2", | ||
"grunt": "^1.0.3", | ||
"chai": "^4.2.0", | ||
"coveralls": "^3.0.9", | ||
"grunt": "^1.0.4", | ||
"grunt-contrib-concat": "^1.0.1", | ||
"grunt-string-replace": "^1.3.1", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^5.2.0" | ||
"mocha": "^7.1.0", | ||
"nyc": "^15.0.0" | ||
}, | ||
@@ -42,8 +42,7 @@ "engines": { | ||
"scripts": { | ||
"build": "node_modules/grunt/bin/grunt", | ||
"test": "node node_modules/.bin/mocha", | ||
"coverage": "node node_modules/.bin/istanbul cover _mocha -- -R spec", | ||
"coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls" | ||
"build": "grunt", | ||
"test": "node_modules/.bin/nyc --reporter=html --reporter=text node_modules/.bin/mocha --timeout 15000", | ||
"coverage": "nyc report --reporter=text-lcov | coveralls" | ||
}, | ||
"version": "3.0.6" | ||
"version": "4.0.24" | ||
} |
108
README.md
<p align="center"> | ||
@@ -10,3 +6,3 @@ <img src='https://i.imgur.com/AOfuTLA.png'> | ||
*Version 3.0.6* - [Github](https://github.com/Pamblam/mysql-import/) - [NPM](https://www.npmjs.com/package/mysql-import) | ||
*Version 4.0.24* ([NPM](https://www.npmjs.com/package/mysql-import)) ([Github](https://github.com/Pamblam/mysql-import/)) | ||
@@ -17,68 +13,86 @@ [![Build Status](https://api.travis-ci.org/Pamblam/mysql-import.svg?branch=master)](https://travis-ci.org/Pamblam/mysql-import/) [![Coverage Status](https://coveralls.io/repos/github/Pamblam/mysql-import/badge.svg?branch=master)](https://coveralls.io/github/Pamblam/mysql-import?branch=master) | ||
## Table of Contents | ||
- [Install](#install) | ||
- [TLDR (Example)](#tldr) | ||
- [Methods](#methods) | ||
- [`constructor`](#new-importerhost-user-password-database) | ||
- [`importer.getImported()`](#importerprototypegetimported) | ||
- [`importer.setEncoding(encoding)`](#importerprototypesetencodingencoding) | ||
- [`importer.use(database)`](#importerprototypeusedatabase) | ||
- [`importer.import(...input)`](#importerprototypeimportinput) | ||
- [`importer.disconnect(graceful=true)`](#importerprototypedisconnectgracefultrue) | ||
- [Contributing](#contributing) | ||
## Install | ||
via [NPM](https://www.npmjs.com/package/mysql-import): | ||
``` | ||
$ npm install --save-dev mysql-import | ||
``` | ||
Via [Github](https://github.com/Pamblam/mysql-import/): | ||
``` | ||
git clone https://github.com/Pamblam/mysql-import.git | ||
``` | ||
## Usage | ||
## TLDR: | ||
Include the package. | ||
``` | ||
const host = 'localhost'; | ||
const user = 'root'; | ||
const password = 'password'; | ||
const database = 'mydb'; | ||
const mysql_import = require('mysql-import'); | ||
const mysql_import = require('mysql-import'); | ||
const Importer = require('../mysql-import.js'); | ||
const importer = new Importer({host, user, password, database}); | ||
`mysql-import` exposes one method and a `version` property. `mysql_import.version` is a string showing the current version of the package. | ||
importer.import('path/to/dump.sql').then(()=>{ | ||
var files_imported = importer.getImported(); | ||
console.log('${files_imported.length} SQL file(s) imported.'); | ||
}).catch(err=>{ | ||
console.error(err); | ||
}); | ||
``` | ||
## Methods | ||
#### `mysql-import.config(Object settings)` | ||
#### new Importer({host, user, password[, database]}) | ||
Prepare the package to communicate with your database and handle any errors. This method **must** be called before importing anything. | ||
The constructor requires an object with a `host`, `user`, and `password` parameter. Passing in a database parameter is optional. | ||
The `settings` object has 4 mandatory parameters and 1 optional parameter. | ||
#### Importer.prototype.getImported() | ||
- `host` - (**mandatory**) The MySQL host to connect to. | ||
- `user` - (**mandatory**) The MySQL user to connect with. | ||
- `password` - (**mandatory**) The password for the user. | ||
- `database` - (**mandatory**) The database to connect to. | ||
- `onerror` - (**optional**) Function to handle errors. The function will receive the Error. If not provided the error will be thrown. | ||
Get an array of files imported. | ||
The `config` method returns a new `importer` instance. | ||
#### Importer.prototype.setEncoding(encoding) | ||
#### `importer.import(String filename)` | ||
Set the encoding to use when reading import files. Supported arguments are: `utf8`, `ucs2`, `utf16le`, `latin1`, `ascii`, `base64`, or `hex`. | ||
Import an `.sql` file to the database. | ||
#### Importer.prototype.use(database) | ||
The `import` method returns a Promise which is resolved when the import has completed. This promise is never rejected, if there is an error, the `onerror` function passed to the `config` method is called with the error object passed into it. | ||
Set or change the database to import to. | ||
#### Example | ||
#### Importer.prototype.import(...input) | ||
```js | ||
const mysql_import = require('mysql-import'); | ||
Import an `.sql` file or files into the database. This method will take... | ||
const mydb_importer = mysql_import.config({ | ||
host: 'localhost', | ||
user: 'testuser', | ||
password: 'testpwd', | ||
database: 'mydb', | ||
onerror: err=>console.log(err.message) | ||
}); | ||
await mydb_importer.import('mydb.sql'); | ||
await mydb_importer.import('mydb2.sql'); | ||
- Any number of paths to individual `.sql` files. | ||
``` | ||
importer.import('path/to/dump1.sql', 'path/to/dum2.sql') | ||
``` | ||
- Any number of paths that contain any number of `.sql` files. | ||
``` | ||
importer.import('path/to/mysqldumps/') | ||
``` | ||
- Any number of arrays containing either of the above. | ||
``` | ||
importer.import(['path/to/dump.sql', 'path/to/dumps/']) | ||
``` | ||
- Any combination of any of the above. | ||
// Each database requires it's own importer. | ||
const yourdb_importer = mysql_import.config({ | ||
host: 'localhost', | ||
user: 'testuser', | ||
password: 'testpwd', | ||
database: 'yourdb', | ||
onerror: err=>console.log(err.message) | ||
}); | ||
#### Importer.prototype.disconnect(graceful=true) | ||
// You can use an array to import more than one file at once | ||
await yourdb_importer.import(['yourdb.sql', 'yourdb2.sql']); | ||
Disconnects the connection. If `graceful` is switched to false it will force close any connections. This is called automatically after files are imported so typically *this method should never be required*. | ||
// Or you can give the path to a directory and import every sql file in that path | ||
await yourdb_importer.import('/path/to/my/sql'); | ||
``` | ||
## Contributing | ||
Contributions are more than welcome! Please check out the [Contributing Guidelines](https://github.com/Pamblam/mysql-import/blob/master/CONTRIBUTING.md) for this project. |
@@ -1,62 +0,189 @@ | ||
class importer{ | ||
constructor(settings, err_handler){ | ||
this.settings = settings; | ||
this.conn = null; | ||
this.err_handler = (e)=>{ | ||
err_handler(e); | ||
this.disconnect(); | ||
} | ||
/** | ||
* mysql-import - Importer class | ||
* @version {{ VERSION }} | ||
* https://github.com/Pamblam/mysql-import | ||
*/ | ||
class Importer{ | ||
/** | ||
* new Importer(settings) | ||
* @param {host, user, password[, database]} settings - login credentials | ||
*/ | ||
constructor(settings){ | ||
this._connection_settings = settings; | ||
this._conn = null; | ||
this._encoding = 'utf8'; | ||
this._imported = []; | ||
} | ||
connect(){ | ||
this.conn = this.conn || mysql.createConnection(this.settings); | ||
/** | ||
* Get an array of the imported files | ||
* @returns {Array} | ||
*/ | ||
getImported(){ | ||
return this._imported.slice(0); | ||
} | ||
disconnect(){ | ||
if(!this.conn) return; | ||
try{ | ||
this.conn.end(); | ||
}catch(e){} | ||
this.conn = null; | ||
/** | ||
* Set the encoding to be used for reading the dump files. | ||
* @param string - encoding type to be used. | ||
* @throws {Error} - if unsupported encoding type. | ||
* @returns {undefined} | ||
*/ | ||
setEncoding(encoding){ | ||
var supported_encodings = [ | ||
'utf8', | ||
'ucs2', | ||
'utf16le', | ||
'latin1', | ||
'ascii', | ||
'base64', | ||
'hex' | ||
]; | ||
if(!supported_encodings.includes(encoding)){ | ||
throw new Error("Unsupported encoding: "+encoding); | ||
} | ||
this._encoding = encoding; | ||
} | ||
getSQLFilePaths(paths){ | ||
if(!Array.isArray(paths)) paths = [paths]; | ||
var full_paths = []; | ||
for(var i=paths.length; i--;){ | ||
let exists = fs.existsSync(paths[i]); | ||
if(!exists) continue; | ||
let stat = fs.lstatSync(paths[i]); | ||
let isFile = stat.isFile(); | ||
let isDir = stat.isDirectory(); | ||
if(!isFile && !isDir) continue; | ||
if(isFile){ | ||
if(paths[i].toLowerCase().substring(paths[i].length-4) === '.sql'){ | ||
full_paths.push(path.resolve(paths[i])); | ||
/** | ||
* Set or change the database to be used | ||
* @param string - database name | ||
* @returns {Promise} | ||
*/ | ||
use(database){ | ||
return new Promise((resolve, reject)=>{ | ||
if(!this._conn){ | ||
this._connection_settings.database = database; | ||
return; | ||
} | ||
this._conn.changeUser({database}, err=>{ | ||
if (err){ | ||
reject(err); | ||
}else{ | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Import (an) .sql file(s). | ||
* @param string|array input - files or paths to scan for .sql files | ||
* @returns {Promise} | ||
*/ | ||
import(...input){ | ||
return new Promise(async (resolve, reject)=>{ | ||
try{ | ||
await this._connect(); | ||
var files = await this._getSQLFilePaths(...input); | ||
var error = null; | ||
await slowLoop(files, (file, index, next)=>{ | ||
if(error){ | ||
next(); | ||
return; | ||
} | ||
this._importSingleFile(file).then(()=>{ | ||
next(); | ||
}).catch(err=>{ | ||
error = err; | ||
next(); | ||
}); | ||
}); | ||
if(error) throw error; | ||
await this.disconnect(); | ||
resolve(); | ||
}catch(err){ | ||
reject(err); | ||
} | ||
}); | ||
}; | ||
/** | ||
* Disconnect mysql. This is done automatically, so shouldn't need to be manually called. | ||
* @param bool graceful - force close? | ||
* @returns {Promise} | ||
*/ | ||
disconnect(graceful=true){ | ||
return new Promise((resolve, reject)=>{ | ||
if(!this._conn){ | ||
resolve(); | ||
return; | ||
} | ||
if(graceful){ | ||
this._conn.end(err=>{ | ||
if(err){ | ||
reject(err); | ||
return; | ||
} | ||
this._conn = null; | ||
resolve(); | ||
}); | ||
}else{ | ||
var more_paths = fs.readdirSync(paths[i]).map(p=>path.join(paths[i], p)); | ||
full_paths.push(...this.getSQLFilePaths(more_paths)); | ||
} | ||
} | ||
return full_paths; | ||
this._conn.destroy(); | ||
resolve(); | ||
} | ||
}); | ||
} | ||
importSingleFile(filename){ | ||
return new Promise(done=>{ | ||
var queriesString = fs.readFileSync(filename, 'utf8'); | ||
var queries = new queryParser(queriesString).queries; | ||
slowLoop(queries, (q,i,d)=>{ | ||
try{ | ||
this.conn.query(q, err=>{ | ||
/* istanbul ignore next */ | ||
if (err) this.err_handler(err); | ||
else d(); | ||
//////////////////////////////////////////////////////////////////////////// | ||
// Private methods ///////////////////////////////////////////////////////// | ||
//////////////////////////////////////////////////////////////////////////// | ||
/** | ||
* Import a single .sql file into the database | ||
* @param {type} filepath | ||
* @returns {Promise} | ||
*/ | ||
_importSingleFile(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.readFile(filepath, this._encoding, (err, queriesString) => { | ||
if(err){ | ||
reject(err); | ||
return; | ||
} | ||
var queries = new queryParser(queriesString).queries; | ||
var error = null; | ||
slowLoop(queries, (query, index, next)=>{ | ||
if(error){ | ||
next(); | ||
return; | ||
} | ||
this._conn.query(query, err=>{ | ||
if (err) error = err; | ||
next(); | ||
}); | ||
}catch(e){ | ||
/* istanbul ignore next */ | ||
this.err_handler(e); | ||
}).then(()=>{ | ||
if(error){ | ||
reject(error); | ||
}else{ | ||
this._imported.push(filepath); | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Connect to the mysql server | ||
* @returns {Promise} | ||
*/ | ||
_connect(){ | ||
return new Promise((resolve, reject)=>{ | ||
if(this._conn){ | ||
resolve(this._conn); | ||
return; | ||
} | ||
var connection = mysql.createConnection(this._connection_settings); | ||
connection.connect(err=>{ | ||
if (err){ | ||
reject(err); | ||
}else{ | ||
this._conn = connection; | ||
resolve(); | ||
} | ||
}).then(()=>{ | ||
done(); | ||
}); | ||
@@ -66,37 +193,105 @@ }); | ||
import(input){ | ||
return new Promise(done=>{ | ||
this.connect(); | ||
var files = this.getSQLFilePaths(input); | ||
slowLoop(files, (f,i,d)=>{ | ||
this.importSingleFile(f).then(d); | ||
}).then(()=>{ | ||
this.disconnect(); | ||
done(); | ||
/** | ||
* Check if a file exists | ||
* @param string filepath | ||
* @returns {Promise} | ||
*/ | ||
_fileExists(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.access(filepath, fs.F_OK, err=>{ | ||
if(err){ | ||
reject(err); | ||
}else{ | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
}; | ||
} | ||
/** | ||
* Get filetype information | ||
* @param string filepath | ||
* @returns {Promise} | ||
*/ | ||
_statFile(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.lstat(filepath, (err, stat)=>{ | ||
if(err){ | ||
reject(err); | ||
}else{ | ||
resolve(stat); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
/** | ||
* Read contents of a directory | ||
* @param string filepath | ||
* @returns {Promise} | ||
*/ | ||
_readDir(filepath){ | ||
return new Promise((resolve, reject)=>{ | ||
fs.readdir(filepath, (err, files)=>{ | ||
if(err){ | ||
reject(err); | ||
}else{ | ||
resolve(files); | ||
} | ||
}); | ||
}); | ||
} | ||
importer.version = '{{ VERSION }}'; | ||
importer.config = function(settings){ | ||
const valid = settings.hasOwnProperty('host') && typeof settings.host === "string" && | ||
settings.hasOwnProperty('user') && typeof settings.user === "string" && | ||
settings.hasOwnProperty('password') && typeof settings.password === "string" && | ||
settings.hasOwnProperty('database') && typeof settings.database === "string"; | ||
/* istanbul ignore next */ | ||
if(!settings.hasOwnProperty("onerror") || typeof settings.onerror !== "function"){ | ||
settings.onerror = err=>{ throw err }; | ||
/** | ||
* Parses the input argument(s) for Importer.import into an array sql files. | ||
* @param strings|array paths | ||
* @returns {Promise} | ||
*/ | ||
_getSQLFilePaths(...paths){ | ||
return new Promise(async (resolve, reject)=>{ | ||
var full_paths = []; | ||
var error = null; | ||
paths = [].concat.apply([], paths); // flatten array of paths | ||
await slowLoop(paths, async (filepath, index, next)=>{ | ||
if(error){ | ||
next(); | ||
return; | ||
} | ||
try{ | ||
await this._fileExists(filepath); | ||
var stat = await this._statFile(filepath); | ||
if(stat.isFile()){ | ||
if(filepath.toLowerCase().substring(filepath.length-4) === '.sql'){ | ||
full_paths.push(path.resolve(filepath)); | ||
} | ||
next(); | ||
}else if(stat.isDirectory()){ | ||
var more_paths = await this._readDir(filepath); | ||
more_paths = more_paths.map(p=>path.join(filepath, p)); | ||
var sql_files = await this._getSQLFilePaths(...more_paths); | ||
full_paths.push(...sql_files); | ||
next(); | ||
}else{ | ||
next(); | ||
} | ||
}catch(err){ | ||
error = err; | ||
next(); | ||
} | ||
}); | ||
if(error){ | ||
reject(error); | ||
}else{ | ||
resolve(full_paths); | ||
} | ||
}); | ||
} | ||
} | ||
var err_handler = settings.onerror; | ||
/** | ||
* Build version number | ||
*/ | ||
Importer.version = '{{ VERSION }}'; | ||
/* istanbul ignore next */ | ||
if(!valid) return settings.onerror(new Error("Invalid host, user, password, or database parameters")); | ||
return new importer(settings, err_handler); | ||
}; | ||
module.exports = importer; | ||
module.exports = Importer; |
@@ -1,1 +0,3 @@ | ||
This file is not sql. | ||
select * from notarealtable; | ||
select anything form nowhere; | ||
insert wetwilly into ear; |
@@ -48,6 +48,5 @@ | ||
*/ | ||
async function createTestDB(){ | ||
await query("DROP DATABASE IF EXISTS `testdb`;"); | ||
await query("CREATE DATABASE `testdb`;"); | ||
await query("USE `testdb`;"); | ||
async function createTestDB(db){ | ||
await query("DROP DATABASE IF EXISTS `"+db+"`;"); | ||
await query("CREATE DATABASE `"+db+"`;"); | ||
} | ||
@@ -59,4 +58,7 @@ | ||
*/ | ||
async function destroyTestDB(){ | ||
await query("DROP DATABASE `testdb`;"); | ||
async function destroyTestDB(db){ | ||
await query("DROP DATABASE `"+db+"`;"); | ||
} | ||
function closeConnection(){ | ||
con.end(); | ||
@@ -70,3 +72,4 @@ } | ||
createTestDB, | ||
destroyTestDB | ||
destroyTestDB, | ||
closeConnection | ||
}; |
153
test/test.js
@@ -9,3 +9,3 @@ | ||
const expect = require('chai').expect; | ||
const {errorHandler,query,mysqlConnect,createTestDB,destroyTestDB} = require('./test-helpers.js'); | ||
const {errorHandler,query,mysqlConnect,createTestDB,destroyTestDB,closeConnection} = require('./test-helpers.js'); | ||
@@ -15,5 +15,4 @@ var config = { | ||
user: mysql_user || 'root', | ||
password: mysql_pass || '', | ||
database: 'testdb', | ||
onerror: errorHandler | ||
password: mysql_pass || '', | ||
database: 'mysql-import-test-db-1' | ||
}; | ||
@@ -23,3 +22,6 @@ | ||
const importer = require('../mysql-import.js').config(config); | ||
const fs = require('fs'); | ||
const MySQLImport = require('../mysql-import.js'); | ||
const importer = new MySQLImport(config); | ||
const start_time = new Date(); | ||
@@ -30,8 +32,13 @@ | ||
before(async ()=>{ | ||
await createTestDB(); | ||
await createTestDB('mysql-import-test-db-1'); | ||
await createTestDB('mysql-import-test-db-2'); | ||
query("USE `mysql-import-test-db-1`"); | ||
await importer.import(__dirname+'/sample_dump_files/test.sql'); | ||
importer.setEncoding('utf8'); | ||
}); | ||
after(async ()=>{ | ||
await destroyTestDB(); | ||
await destroyTestDB('mysql-import-test-db-1'); | ||
await destroyTestDB('mysql-import-test-db-2'); | ||
closeConnection(); | ||
console.log(`All tests completed in ${(new Date() - start_time)/1000} seconds.`); | ||
@@ -67,9 +74,137 @@ }); | ||
it('Import Array, Directory', async ()=>{ | ||
await importer.import([ | ||
await importer.import( | ||
__dirname+'/sample_dump_files/test3.sql', | ||
__dirname+'/sample_dump_files/more_sample_files/' | ||
]); | ||
); | ||
var tables = await query("SHOW TABLES;"); | ||
expect(tables.length).to.equal(6); | ||
}); | ||
it('Change database', async ()=>{ | ||
query("USE `mysql-import-test-db-2`;"); | ||
importer.use('mysql-import-test-db-2'); | ||
await importer.import(__dirname+'/sample_dump_files/'); | ||
var tables = await query("SHOW TABLES;"); | ||
expect(tables.length).to.equal(6); | ||
}); | ||
it('Test unsupported encoding', ()=>{ | ||
var error; | ||
try{ | ||
importer.setEncoding("#we&%"); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test manually connecting', async ()=>{ | ||
var host = config.host; | ||
var error = null; | ||
try{ | ||
importer._connection_settings.host = "#$%^"; | ||
await importer._connect(); | ||
}catch(e){ | ||
error = e; | ||
importer._connection_settings.host = host; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test live DB change', async ()=>{ | ||
await importer._connect(); | ||
await importer._connect(); // a second time time, intentionally | ||
await importer.use('mysql-import-test-db-1'); // should work with no problems | ||
var error; | ||
try{ | ||
await importer.use('mysql-import-test-#$%'); | ||
}catch(e){ | ||
error = e; | ||
} | ||
try{ await importer.disconnect(true); }catch(e){} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Single file error handling', async ()=>{ | ||
var error; | ||
try{ | ||
await importer.importSingleFile("@#$"); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test fake sql file.', async ()=>{ | ||
var fake_sql_file = __dirname+"/sample_dump_files/more_sample_files/not_sql.txt"; | ||
var error; | ||
try{ | ||
await importer.importSingleFile(fake_sql_file); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test importing broken file.', async ()=>{ | ||
var fake_sql_file = __dirname+"/broken_dump_files/dump.sql"; | ||
var fake_sql_file2 = __dirname+"/broken_dump_files/dump_1.sql"; | ||
var error; | ||
try{ | ||
await importer.import(fake_sql_file, fake_sql_file2); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test diconnect function.', async ()=>{ | ||
try{ | ||
importer._conn = false; | ||
await importer.disconnect(); | ||
await importer._connect(); | ||
await importer.disconnect(false); | ||
}catch(e){} | ||
}); | ||
it('Test fileExist method.', async ()=>{ | ||
var error; | ||
try{ | ||
await importer._fileExists('!@#$'); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test statFile method.', async ()=>{ | ||
var error; | ||
try{ | ||
await importer._statFile('!@#$'); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Test readDir method.', async ()=>{ | ||
var error; | ||
try{ | ||
await importer._readDir('!@#$'); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
it('Testing path parser.', async ()=>{ | ||
var error; | ||
try{ | ||
await importer._getSQLFilePaths('!@#$', '$%^#^', __dirname+"/broken_dump_files"); | ||
}catch(e){ | ||
error = e; | ||
} | ||
expect(typeof error).to.equal("object"); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
158741
21
1082
97
3
Updatedmysql@^2.18.1