UNPKG

ravel

Version:

Ravel Rapid Application Development Framework

89 lines (86 loc) 6.74 kB
'use strict'; const Metadata = require('../../util/meta'); const ApplicationError = require('../../util/application_error'); /** * The `@transaction` decorator for opening a transaction on a `Routes` or * `Resource` handler method. Facilitates transaction-per-request. * Can also be applied at the class-level to open connections for all handlers * in that `Route` or `Resource` class. * * Connections are available within the handler as an object `ctx.transaction`, which * contains connections as values and `DatabaseProvider` names as keys. Connections * will be closed automatically when the endpoint responds (do not close them yourself), * and will automatically roll-back changes if a `DatabaseProvider` supports it (generally * a SQL-only feature). * * @param {...String} args - A list of database provider names to open connections for. * * @example * // Note: decorator works the same way on Routes or Resource classes * const Routes = require('ravel').Routes; * const mapping = Routes.mapping; * * class MyRoutes extends Routes { * constructor () { * super('/'); * } * * // @mapping(Routes.GET, 'app') * // @transaction // open all connections within this handler * async appHandler (ctx) { * // ctx.transaction is an object containing * // keys for DatabaseProviders and values * // for their open connections * } * * // @mapping(Routes.GET, 'something') * // @transaction('mysql', 'rethinkdb') // open one or more specific connections within this handler * async somethingHandler (ctx) { * // can use ctx.transaction.mysql * // can use ctx.transaction.rethinkdb * } * } * @example * // Note: decorator works the same way on Routes or Resource classes * const Resource = require('ravel').Resource; * * // @transaction('mysql') // all handlers will have ctx.transaction.mysql * class MyResource extends Resource { * constructor () { * super('/'); * } * * async post (ctx) { * // can use ctx.transaction.mysql * } * } */ function transaction(...args) { // handle @transaction at the method-level without arguments if (args.length === 3 && typeof args[0].constructor === 'function') { Metadata.putMethodMeta(args[0], args[1], '@transaction', 'providers', []); } else if (args.length === 1 && typeof args[0] === 'function') { // handle @transaction at the class-level without arguments Metadata.putClassMeta(args[0].prototype, '@transaction', 'providers', []); } else { // handle @transaction() at the class and method-level with arguments return function (target, key) { args.forEach(name => { if (typeof name !== 'string') { throw new ApplicationError.IllegalValue( 'Values supplied to @transaction decorator must be strings, and must match ' + 'the name of a registered database provider'); } }); if (key === undefined) { Metadata.putClassMeta(target.prototype, '@transaction', 'providers', args); } else { Metadata.putMethodMeta(target, key, '@transaction', 'providers', args); } }; } } /*! * Export the `@transaction` decorator */ module.exports = transaction;