From 59b970ad5ad0f4d4ce41244ff03e46e6d45c280d Mon Sep 17 00:00:00 2001 From: Alex Robson Date: Fri, 4 Dec 2015 00:48:53 -0600 Subject: [PATCH] Added connectivity events --- CHANGELOG.md | 4 ++++ README.md | 9 +++++++ package.json | 4 ++-- spec/integration/seriate.spec.js | 40 ++++++++++++++++++++++++++++++-- src/connections.js | 28 ++++++++++++++-------- src/index.js | 25 ++++++++++++++++++-- 6 files changed, 94 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe2479c..499fad1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## 0.5.* +### 0.5.5 + * Emit connectivity events + * Prevent reading sql files multiple times + ### 0.5.4 Update mssql to 2.3.2 diff --git a/README.md b/README.md index aa95931..90070d7 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,15 @@ A connection configuration provides information on how to connect to the datbase } ``` +### Connectivity Events +Seriate emits connectivity events from the top level library: + + * connected + * closed + * failed + +Each event includes a `name` property that represents which connection the event is happening on. If the event is failed, an `error` property will have the error that caused the failure. + ## API Sql type constants are exposed in both Pascal Case and all capitals off of the library. See the listing at the end of this document. diff --git a/package.json b/package.json index 8a5a817..349b6e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "seriate", - "version": "0.5.4", + "version": "0.5.5", "description": "A cross platform module for Microsoft SQL Server that wraps node-mssql", "main": "src/index.js", "author": "LeanKit", @@ -44,7 +44,7 @@ "configya": "~0.2.1", "lodash": "3.x", "machina": "1.x", - "monologue.js": "~0.3.1", + "monologue.js": "~0.3.3", "mssql": "~2.3.2", "postal": "^1.0.6", "when": "3.x", diff --git a/spec/integration/seriate.spec.js b/spec/integration/seriate.spec.js index b6039ac..b8a4187 100644 --- a/spec/integration/seriate.spec.js +++ b/spec/integration/seriate.spec.js @@ -41,7 +41,7 @@ function deleteTestRows( sql ) { } describe( "Seriate Integration Tests", function() { - var sql; + var sql, connected; before( function() { this.timeout( 20000 ); sql = proxyquire( "../src/index", {} ); @@ -73,12 +73,23 @@ describe( "Seriate Integration Tests", function() { .then( function() {} ); } + sql.once( "connected", function() { + connected = true; + } ); + return dropDatabase() .then( createDatabase ) .then( setupPrerequisites ); } ); - after( function() { + it( "should connect successfully", function() { + connected.should.equal.true; + } ); + + after( function( done ) { + sql.once( "closed", function( connection ) { + done(); + } ); sql.closeConnection( "default" ); sql.closeConnection( "master" ); } ); @@ -368,6 +379,7 @@ describe( "Seriate Integration Tests", function() { procResults.returnValue.should.equal( 0 ); } ); } ); + describe( "when retrieving multiple record sets", function() { before( function() { return insertTestRows( sql ); @@ -439,6 +451,7 @@ describe( "Seriate Integration Tests", function() { multipleResults.returnValue.should.equal( 0 ); } ); } ); + describe( "with stored procedures", function() { var multipleRSProcResults; @@ -466,4 +479,27 @@ describe( "Seriate Integration Tests", function() { } ); } ); } ); + + describe( "when failing to connect", function() { + var failed; + before( function( done ) { + sql.once( "failed", function( connection ) { + failed = connection; + done(); + } ); + + sql.addConnection( { + name: "lol", + host: "lol", + user: "lol", + password: "lol", + database: "lol" + } ); + } ); + + it( "should emit failed with an error", function() { + failed.name.should.equal( "lol" ); + failed.should.have.property( "error" ); + } ); + } ); } ); diff --git a/src/connections.js b/src/connections.js index d99f0d0..3852be1 100644 --- a/src/connections.js +++ b/src/connections.js @@ -1,6 +1,7 @@ var _ = require( "lodash" ); var when = require( "when" ); var sql = require( "mssql" ); +var Monologue = require( "monologue.js" ).prototype; var log = require( "./log" )( "seriate.connection" ); var state = { @@ -10,6 +11,14 @@ var state = { aliases: {} }; +var api = _.assign( { + state: state, + add: addConnection, + close: closeConnection, + get: getConnection, + reset: resetState +}, Monologue ); + function addConnection( config ) { var name = getName( config ); var original = getConfiguration( name ); @@ -52,28 +61,33 @@ function connect( name, config ) { pool = state.pools[ name ] = new sql.Connection( config ); pool.on( "connect", function() { + api.emit( "connected", { name: name } ); log.info( "Connected to \"%s\"", name ); } ); pool.on( "close", function() { + api.emit( "closed", { name: name } ); log.info( "Closed connection to \"%s\"", name ); pool.removeAllListeners(); delete state.connections[ name ]; delete state.pools[ name ]; } ); - pool.on( "error", function( err ) { + function onConnectionError( err ) { + api.emit( "failed", { name: name, error: err } ); log.error( "Failed to connection to \"%s\" with: %s", name, err ); delete state.connections[ name ]; delete state.pools[ name ]; pool.removeAllListeners(); - } ); + } + + pool.on( "error", onConnectionError ); state.pools[ name ] = pool; state.connections[ name ] = pool.connect() .then( function() { return pool; - } ); + }, onConnectionError ); return state.connections[ name ]; } @@ -147,10 +161,4 @@ function resetState() { }; } -module.exports = { - state: state, - add: addConnection, - close: closeConnection, - get: getConnection, - reset: resetState -}; +module.exports = api; diff --git a/src/index.js b/src/index.js index b6749c0..71a468c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,14 @@ var when = require( "when" ); var fs = require( "fs" ); var _ = require( "lodash" ); +var Monologue = require( "monologue.js" ).prototype; var path = require( "path" ); var callsite = require( "callsite" ); var sql = require( "mssql" ); var connections = require( "./connections" ); var SqlContext = require( "./sqlContext" )(); var TransactionContext = require( "./transactionContext" )( SqlContext ); +var fileCache = {}; function promisify( context, queryOptions ) { var name = queryOptions.name || queryOptions.procedure || "__result__"; @@ -84,7 +86,12 @@ var seriate = { p = this._getFilePath( p ); var ext = path.extname( p ); p = ( ext === "." ) ? ( p + "sql" ) : ( ext.length === 0 ) ? p + ".sql" : p; - return fs.readFileSync( p, { encoding: "utf8" } ); + var content = fileCache[ p ]; + if ( _.isEmpty( content ) ) { + content = fs.readFileSync( p, { encoding: "utf8" } ); + fileCache[ p ] = content; + } + return content; }, addConnection: function( config ) { connections.add( config ); @@ -114,4 +121,18 @@ _.each( sql.TYPES, function( val, key ) { seriate[ key.toUpperCase() ] = sql.TYPES[ key ]; } ); -module.exports = seriate; +var api = _.assign( seriate, Monologue ); + +connections.on( "connected", function( info ) { + api.emit( "connected", info ); +} ); + +connections.on( "closed", function( info ) { + api.emit( "closed", info ); +} ); + +connections.on( "failed", function( info ) { + api.emit( "failed", info ); +} ); + +module.exports = api;