Promise = require 'bluebird'
_ = require 'lodash'
{NEW, SUB, AUTO, IMPLICIT} = require './constants'
Transaction = require './transaction'
Request = require './request'
Logger = require 'logger-facade-nodejs'
fs = require 'fs'
Implements transaction management using two classes: Transaction
which represents transactions themselves, and Request
which represent
requests for transactions, and wrap requests for database clients.
It exports a facade defined in this file.
Promise = require 'bluebird'
_ = require 'lodash'
{NEW, SUB, AUTO, IMPLICIT} = require './constants'
Transaction = require './transaction'
Request = require './request'
Logger = require 'logger-facade-nodejs'
fs = require 'fs'
Logger output: if not otherwise configured, output to console, level “WARN”
if Logger.plugins().length == 0
Logger.use new (require 'logger-facade-console-plugin-nodejs') {
level: 'info'
timeFormat: 'MM:ss.SSS'
messageFormat: "%time: %logger: %msg"
}
By default, we use logger named “xwrap”. Override using “setLogger” function.
logger = Logger.getLogger('xwrap')
Request.logger = logger
Transaction.logger = logger
adapters = {}
All aruments can be passed in a hash. If the first argument is not a hash,
then, arguments are interpreted based on which argument is callable: the
first callable argument is taken to be the callback, and some or
all of type
, id
and name
may be undefined depending on the
order of the callback.
type
: Type of transaction requested:
NEW
a new top-level transactionSUB
a sub-transactionAUTO
an “autocommit” (dummy) transaction, which doesn’t wrap
sub-calls.IMPLICIT
(default) either new or sub transaction, depending on
whether we are wrapped by another open transaction.name
: name associated with the caller who opened the transaction.
Specify for logging and debugging.
callback
: callback in which to wrap database calls in transaction.
—
module.exports = initializer = ({adapter, settings, id, wrap})->
adapterName = adapter
adapter = undefined
wrap ?= true
xtransaction = ()->
if typeof arguments[0] == 'object'
{type, callback, name} = type
for arg, i in arguments
if typeof arg == 'function'
callback = arg
[type, name] = Array::slice.call(arguments, 0, i)
break
if !callback?
new Error('callback must be specified')
Transaction.create({callback, type, name, adapter, id})
adapter = resolveAdapter(adapterName, settings, id)
adapter.id = id
adapter.xtransaction = xtransaction
adapters[id] = adapter
findAdapterFeatures(adapter)
NEED TO FIX & TEST WRAPPING if wrap? and adapter.features.xwrap.wrap adapter.wrap
add a full xwrap interface to transaction, but specialized to the id of the adapter.
xtransaction.client = (callerName)->
return initializer.client(id, callerName)
xtransaction.takeClient = (callerName)->
return initializer.takeClient(id, callerName)
xtransaction.getTransaction = (callerName)->
return initializer.getTransaction(id, callerName)
xtransaction.disconnect = ()->
return initializer.disconnect(id)
xtransaction.NEW = NEW
xtransaction.SUB = SUB
xtransaction.AUTO = AUTO
xtransaction.Transaction = Transaction
xtransaction.Request = Request
xtransaction.adapter = adapter
xtransaction.id = id
return xtransaction
Load and initialize an adapter, given name, settings and ID.
resolveAdapter = (name, settings, id)->
adapter = adapters[id]
return adapter if adapter?
try
switch
when typeof name == 'object'
@name = name.name
return name
when name.match(/^\//)
adapterClass = require(name)
when fs.existsSync(__dirname + '/adapters/' + name + '.js') ||
fs.existsSync(__dirname + '/adapters/' + name + '.coffee') ||
fs.existsSync(__dirname + '/adapters/' + name + '.litcoffee')
adapterMod = require './adapters/' + name
else
adapterMod = require "xwrap-#{name}"
return adapterMod.initialize(settings)
catch e
if e.message.indexOf('Cannot find module') != -1
throw new Error("XWrap adapter '#{name}' not found.")
else
throw e
Check adapter for interfaces, and set features if not set.
BASIC_INTERFACE = [
'getRawClient', 'openTransaction', 'commitTransaction', 'rollbackTransaction' ]
SUBTRANSACTIONS_INTERFACE = [
'openSubTransaction', 'commitSubTransaction', 'rollbackSubTransaction' ]
WRAP_INTERACE = ['wrap']
findAdapterFeatures = (adapter)->
features = (adapter.features ?= {})
xwrapFeatures = (features.xwrap ?= {})
xwrapFeatures.clientMethods ?= ['query']
xwrapFeatures.clientDataAttributes ?= []
for api in ['basic', 'subtransactions', 'wrap']
continue if xwrapFeatures[api]?
methods = switch api
when 'basic' then BASIC_INTERFACE
when 'subtransactions' then SUBTRANSACTIONS_INTERFACE
when 'wrap' then WRAP_INTERACE
missing = false
for method in methods
if !adapter[method]?
missing = true
break
xwrapFeatures[api] = !missing
Return adapter for id, or “the” adapter if there is only one
initializer.getAdapter = getAdapter = (id)->
return adapters[id] if id?
switch _.size(adapters)
when 1 then return _.values(adapters)[0]
when 0 then null
else
throw new Error('Must specify adapter id when more than one.')
Add xwrap interface to initializer function.
Get a client — either a client in a transaction or any client if no active transaction.
initializer.client = (id, callerName)->
return Request.client(id, callerName).then (client)->
return client ? getAdapter(id).getRawClient()
Take client from transaction — other users of transaction will wait. Wrap in “Promise.using” to insure client is returned.
Returns null if no active transaction.
initializer.takeClient = (id, callerName)->
return Request.takeClient(id, callerName)
initializer.xtransaction = (id, callerName)->
adapter = getAdapter(id)
adapter.xtransaction
Disconnect from data source; return promise fulfilled when disconnection complete.
initializer.disconnect = (id)->
adapter = getAdapter(id)
return Promise.resolve() if !adapter?
if id?
delete adapters[id]
else
must be only one or “getAdapter” will raise an error
delete adapters[Object.keys(adapters)[0]]
adapter?.disconnect?()
initializer.NEW = NEW
initializer.SUB = SUB
initializer.AUTO = AUTO
initializer.Transaction = Transaction
initializer.Request = Request
initializer.BASIC_INTERFACE = BASIC_INTERFACE
initializer.SUBTRANSACTIONS_INTERFACE = SUBTRANSACTIONS_INTERFACE
initializer.WRAP_INTERACE = WRAP_INTERACE
initializer.useLogger = (logger_)->
Request.logger = Transaction.logger = logger = logger_